|
@@ -65,6 +65,7 @@ import static org.elasticsearch.repositories.blobstore.testkit.ContendedRegister
|
|
import static org.elasticsearch.repositories.blobstore.testkit.ContendedRegisterAnalyzeAction.longFromBytes;
|
|
import static org.elasticsearch.repositories.blobstore.testkit.ContendedRegisterAnalyzeAction.longFromBytes;
|
|
import static org.hamcrest.Matchers.allOf;
|
|
import static org.hamcrest.Matchers.allOf;
|
|
import static org.hamcrest.Matchers.anEmptyMap;
|
|
import static org.hamcrest.Matchers.anEmptyMap;
|
|
|
|
+import static org.hamcrest.Matchers.anyOf;
|
|
import static org.hamcrest.Matchers.containsString;
|
|
import static org.hamcrest.Matchers.containsString;
|
|
import static org.hamcrest.Matchers.equalTo;
|
|
import static org.hamcrest.Matchers.equalTo;
|
|
import static org.hamcrest.Matchers.nullValue;
|
|
import static org.hamcrest.Matchers.nullValue;
|
|
@@ -387,6 +388,30 @@ public class RepositoryAnalysisFailureIT extends AbstractSnapshotIntegTestCase {
|
|
);
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ public void testFailsIfEmptyRegisterRejected() {
|
|
|
|
+ final RepositoryAnalyzeAction.Request request = new RepositoryAnalyzeAction.Request("test-repo");
|
|
|
|
+ blobStore.setDisruption(new Disruption() {
|
|
|
|
+ @Override
|
|
|
|
+ public boolean acceptsEmptyRegister() {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ final var exception = expectThrows(RepositoryVerificationException.class, () -> analyseRepository(request));
|
|
|
|
+ assertThat(exception.getMessage(), containsString("analysis failed"));
|
|
|
|
+ final var cause = ExceptionsHelper.unwrapCause(exception.getCause());
|
|
|
|
+ if (cause instanceof IOException ioException) {
|
|
|
|
+ assertThat(ioException.getMessage(), containsString("empty register update rejected"));
|
|
|
|
+ } else {
|
|
|
|
+ assertThat(
|
|
|
|
+ asInstanceOf(RepositoryVerificationException.class, ExceptionsHelper.unwrapCause(exception.getCause())).getMessage(),
|
|
|
|
+ anyOf(
|
|
|
|
+ allOf(containsString("uncontended register operation failed"), containsString("did not observe any value")),
|
|
|
|
+ containsString("but instead had value [OptionalBytesReference[MISSING]]")
|
|
|
|
+ )
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
private void analyseRepository(RepositoryAnalyzeAction.Request request) {
|
|
private void analyseRepository(RepositoryAnalyzeAction.Request request) {
|
|
client().execute(RepositoryAnalyzeAction.INSTANCE, request).actionGet(30L, TimeUnit.SECONDS);
|
|
client().execute(RepositoryAnalyzeAction.INSTANCE, request).actionGet(30L, TimeUnit.SECONDS);
|
|
}
|
|
}
|
|
@@ -508,6 +533,10 @@ public class RepositoryAnalysisFailureIT extends AbstractSnapshotIntegTestCase {
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ default boolean acceptsEmptyRegister() {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
default BytesReference onContendedCompareAndExchange(BytesRegister register, BytesReference expected, BytesReference updated) {
|
|
default BytesReference onContendedCompareAndExchange(BytesRegister register, BytesReference expected, BytesReference updated) {
|
|
return register.compareAndExchange(expected, updated);
|
|
return register.compareAndExchange(expected, updated);
|
|
}
|
|
}
|
|
@@ -682,7 +711,13 @@ public class RepositoryAnalysisFailureIT extends AbstractSnapshotIntegTestCase {
|
|
) {
|
|
) {
|
|
assertPurpose(purpose);
|
|
assertPurpose(purpose);
|
|
final boolean isContendedRegister = isContendedRegisterKey(key); // validate key
|
|
final boolean isContendedRegister = isContendedRegisterKey(key); // validate key
|
|
- if (disruption.compareAndExchangeReturnsWitness(key)) {
|
|
|
|
|
|
+ if (disruption.acceptsEmptyRegister() == false && updated.length() == 0) {
|
|
|
|
+ if (randomBoolean()) {
|
|
|
|
+ listener.onResponse(OptionalBytesReference.MISSING);
|
|
|
|
+ } else {
|
|
|
|
+ listener.onFailure(new IOException("empty register update rejected"));
|
|
|
|
+ }
|
|
|
|
+ } else if (disruption.compareAndExchangeReturnsWitness(key)) {
|
|
final var register = registers.computeIfAbsent(key, ignored -> new BytesRegister());
|
|
final var register = registers.computeIfAbsent(key, ignored -> new BytesRegister());
|
|
if (isContendedRegister) {
|
|
if (isContendedRegister) {
|
|
listener.onResponse(OptionalBytesReference.of(disruption.onContendedCompareAndExchange(register, expected, updated)));
|
|
listener.onResponse(OptionalBytesReference.of(disruption.onContendedCompareAndExchange(register, expected, updated)));
|