|
@@ -21,7 +21,6 @@ import org.elasticsearch.action.support.HandledTransportAction;
|
|
|
import org.elasticsearch.common.Strings;
|
|
|
import org.elasticsearch.common.blobstore.BlobContainer;
|
|
|
import org.elasticsearch.common.blobstore.BlobPath;
|
|
|
-import org.elasticsearch.common.blobstore.ConcurrentRegisterOperationException;
|
|
|
import org.elasticsearch.common.inject.Inject;
|
|
|
import org.elasticsearch.common.io.stream.StreamInput;
|
|
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
|
@@ -36,6 +35,7 @@ import org.elasticsearch.transport.TransportService;
|
|
|
|
|
|
import java.io.IOException;
|
|
|
import java.util.Map;
|
|
|
+import java.util.OptionalLong;
|
|
|
import java.util.concurrent.ExecutorService;
|
|
|
|
|
|
/**
|
|
@@ -67,7 +67,8 @@ public class RegisterAnalyzeAction extends ActionType<ActionResponse.Empty> {
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- protected void doExecute(Task task, Request request, ActionListener<ActionResponse.Empty> listener) {
|
|
|
+ protected void doExecute(Task task, Request request, ActionListener<ActionResponse.Empty> outerListenerOld) {
|
|
|
+ final var outerListener = ActionListener.assertOnce(outerListenerOld);
|
|
|
final Repository repository = repositoriesService.repository(request.getRepositoryName());
|
|
|
if (repository instanceof BlobStoreRepository == false) {
|
|
|
throw new IllegalArgumentException("repository [" + request.getRepositoryName() + "] is not a blob-store repository");
|
|
@@ -83,59 +84,72 @@ public class RegisterAnalyzeAction extends ActionType<ActionResponse.Empty> {
|
|
|
|
|
|
assert task instanceof CancellableTask;
|
|
|
|
|
|
- ActionListener.run(listener.<Void>map(ignored -> ActionResponse.Empty.INSTANCE), l -> {
|
|
|
- final String registerName = request.getRegisterName();
|
|
|
- long initialValue;
|
|
|
- try {
|
|
|
- initialValue = blobContainer.getRegister(registerName);
|
|
|
- } catch (ConcurrentRegisterOperationException e) {
|
|
|
- // Concurrent activity prevents us from even reading the initial value, but this is just a best-effort thing so we can
|
|
|
- // proceed anyway.
|
|
|
- initialValue = 0;
|
|
|
- } catch (UnsupportedOperationException e) {
|
|
|
- // Registers are not supported on all repository types, and that's ok. If it's not supported here then the final check
|
|
|
- // will also be unsupported, so it doesn't matter that we didn't do anything before this successful response.
|
|
|
- l.onResponse(null);
|
|
|
- return;
|
|
|
- }
|
|
|
+ final String registerName = request.getRegisterName();
|
|
|
+ blobContainer.getRegister(registerName, new ActionListener<>() {
|
|
|
+ @Override
|
|
|
+ public void onResponse(OptionalLong initialValueOrNull) {
|
|
|
+ final long initialValue = initialValueOrNull.orElse(0L);
|
|
|
|
|
|
- if (initialValue < 0 || initialValue >= request.getRequestCount()) {
|
|
|
- throw new IllegalStateException("register holds unexpected value [" + initialValue + "]");
|
|
|
- }
|
|
|
+ ActionListener.run(outerListener.<Void>map(ignored -> ActionResponse.Empty.INSTANCE), l -> {
|
|
|
+ if (initialValue < 0 || initialValue >= request.getRequestCount()) {
|
|
|
+ throw new IllegalStateException("register holds unexpected value [" + initialValue + "]");
|
|
|
+ }
|
|
|
|
|
|
- class Execution extends ActionRunnable<Void> {
|
|
|
- private long currentValue;
|
|
|
+ // noinspection OptionalUsedAsFieldOrParameterType
|
|
|
+ class Execution extends ActionRunnable<Void> {
|
|
|
+ private long currentValue;
|
|
|
|
|
|
- Execution(long currentValue) {
|
|
|
- super(l);
|
|
|
- this.currentValue = currentValue;
|
|
|
- }
|
|
|
+ private final ActionListener<OptionalLong> witnessListener;
|
|
|
|
|
|
- @Override
|
|
|
- protected void doRun() throws Exception {
|
|
|
- if (((CancellableTask) task).notifyIfCancelled(listener)) {
|
|
|
- return;
|
|
|
- }
|
|
|
+ Execution(long currentValue) {
|
|
|
+ super(l);
|
|
|
+ this.currentValue = currentValue;
|
|
|
+ this.witnessListener = listener.delegateFailure(this::handleWitness);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected void doRun() {
|
|
|
+ if (((CancellableTask) task).notifyIfCancelled(listener) == false) {
|
|
|
+ blobContainer.compareAndExchangeRegister(registerName, currentValue, currentValue + 1, witnessListener);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- try {
|
|
|
- final var witness = blobContainer.compareAndExchangeRegister(registerName, currentValue, currentValue + 1);
|
|
|
- if (witness == currentValue) {
|
|
|
- listener.onResponse(null);
|
|
|
- } else if (witness < currentValue || witness >= request.getRequestCount()) {
|
|
|
- throw new IllegalStateException("register holds unexpected value [" + witness + "]");
|
|
|
- } else {
|
|
|
- currentValue = witness;
|
|
|
- executor.execute(Execution.this);
|
|
|
+ private void handleWitness(ActionListener<Void> delegate, OptionalLong witnessOrEmpty) {
|
|
|
+ if (witnessOrEmpty.isEmpty()) {
|
|
|
+ // Concurrent activity prevented us from updating the value, or even reading the concurrently-updated
|
|
|
+ // result, so we must just try again.
|
|
|
+ executor.execute(Execution.this);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ final long witness = witnessOrEmpty.getAsLong();
|
|
|
+ if (witness == currentValue) {
|
|
|
+ delegate.onResponse(null);
|
|
|
+ } else if (witness < currentValue || witness >= request.getRequestCount()) {
|
|
|
+ delegate.onFailure(new IllegalStateException("register holds unexpected value [" + witness + "]"));
|
|
|
+ } else {
|
|
|
+ currentValue = witness;
|
|
|
+ executor.execute(Execution.this);
|
|
|
+ }
|
|
|
}
|
|
|
- } catch (ConcurrentRegisterOperationException e) {
|
|
|
- // Concurrent activity prevented us from updating the value, or even reading the concurrently-updated result,
|
|
|
- // so we must just try again.
|
|
|
- executor.execute(Execution.this);
|
|
|
+
|
|
|
}
|
|
|
- }
|
|
|
+
|
|
|
+ new Execution(initialValue).run();
|
|
|
+
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
- new Execution(initialValue).run();
|
|
|
+ @Override
|
|
|
+ public void onFailure(Exception e) {
|
|
|
+ if (e instanceof UnsupportedOperationException) {
|
|
|
+ // Registers are not supported on all repository types, and that's ok. If it's not supported here then the final
|
|
|
+ // check will also be unsupported, so it doesn't matter that we didn't do anything before this successful response.
|
|
|
+ outerListener.onResponse(ActionResponse.Empty.INSTANCE);
|
|
|
+ } else {
|
|
|
+ outerListener.onFailure(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
});
|
|
|
}
|
|
|
}
|