-
Notifications
You must be signed in to change notification settings - Fork 24.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for cancelling async requests in low-level REST client (#…
…45379) The low-level REST client exposes a `performRequestAsync` method that allows to send async requests, but today it does not expose the ability to cancel such requests. That is something that the underlying apache async http client supports, and it makes sense for us to expose. This commit adds a return value to the `performRequestAsync` method, which is backwards compatible. A `Cancellable` object gets returned, which exposes a `cancel` public method. When calling `cancel`, the on-going request associated with the returned `Cancellable` instance will be cancelled by calling its `abort` method. This works throughout multiple retries, though some special care was needed for the case where `cancel` is called between different attempts (when one attempt has failed and the consecutive one has not been sent yet). Note that cancelling a request on the client side does not automatically translate to cancelling the server side execution of it. That needs to be specifically implemented, which is on the work for the search API (see #43332). Relates to #44802
- Loading branch information
Showing
8 changed files
with
366 additions
and
86 deletions.
There are no files selected for viewing
87 changes: 87 additions & 0 deletions
87
client/rest/src/main/java/org/elasticsearch/client/Cancellable.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/* | ||
* Licensed to Elasticsearch under one or more contributor | ||
* license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright | ||
* ownership. Elasticsearch licenses this file to you under | ||
* the Apache License, Version 2.0 (the "License"); you may | ||
* not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
package org.elasticsearch.client; | ||
|
||
import org.apache.http.client.methods.AbstractExecutionAwareRequest; | ||
import org.apache.http.client.methods.HttpRequestBase; | ||
|
||
import java.util.concurrent.CancellationException; | ||
|
||
/** | ||
* Represents an operation that can be cancelled. | ||
* Returned when executing async requests through {@link RestClient#performRequestAsync(Request, ResponseListener)}, so that the request | ||
* can be cancelled if needed. Cancelling a request will result in calling {@link AbstractExecutionAwareRequest#abort()} on the underlying | ||
* request object, which will in turn cancel its corresponding {@link java.util.concurrent.Future}. | ||
* Note that cancelling a request does not automatically translate to aborting its execution on the server side, which needs to be | ||
* specifically implemented in each API. | ||
*/ | ||
public class Cancellable { | ||
|
||
static final Cancellable NO_OP = new Cancellable(null) { | ||
@Override | ||
public void cancel() { | ||
} | ||
|
||
@Override | ||
void runIfNotCancelled(Runnable runnable) { | ||
throw new UnsupportedOperationException(); | ||
} | ||
}; | ||
|
||
static Cancellable fromRequest(HttpRequestBase httpRequest) { | ||
return new Cancellable(httpRequest); | ||
} | ||
|
||
private final HttpRequestBase httpRequest; | ||
|
||
private Cancellable(HttpRequestBase httpRequest) { | ||
this.httpRequest = httpRequest; | ||
} | ||
|
||
/** | ||
* Cancels the on-going request that is associated with the current instance of {@link Cancellable}. | ||
* | ||
*/ | ||
public synchronized void cancel() { | ||
this.httpRequest.abort(); | ||
} | ||
|
||
/** | ||
* Executes some arbitrary code iff the on-going request has not been cancelled, otherwise throws {@link CancellationException}. | ||
* This is needed to guarantee that cancelling a request works correctly even in case {@link #cancel()} is called between different | ||
* attempts of the same request. The low-level client reuses the same instance of the {@link AbstractExecutionAwareRequest} by calling | ||
* {@link AbstractExecutionAwareRequest#reset()} between subsequent retries. The {@link #cancel()} method can be called at anytime, | ||
* and we need to handle the case where it gets called while there is no request being executed as one attempt may have failed and | ||
* the subsequent attempt has not been started yet. | ||
* If the request has already been cancelled we don't go ahead with the next attempt, and artificially raise the | ||
* {@link CancellationException}, otherwise we run the provided {@link Runnable} which will reset the request and send the next attempt. | ||
* Note that this method must be synchronized as well as the {@link #cancel()} method, to prevent a request from being cancelled | ||
* when there is no future to cancel, which would make cancelling the request a no-op. | ||
*/ | ||
synchronized void runIfNotCancelled(Runnable runnable) { | ||
if (this.httpRequest.isAborted()) { | ||
throw newCancellationException(); | ||
} | ||
runnable.run(); | ||
} | ||
|
||
static CancellationException newCancellationException() { | ||
return new CancellationException("request was cancelled"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.