|
@@ -21,6 +21,7 @@ package org.elasticsearch.index.shard;
|
|
|
|
|
|
import org.apache.logging.log4j.Logger;
|
|
import org.apache.logging.log4j.Logger;
|
|
import org.apache.logging.log4j.message.ParameterizedMessage;
|
|
import org.apache.logging.log4j.message.ParameterizedMessage;
|
|
|
|
+import org.elasticsearch.Assertions;
|
|
import org.elasticsearch.common.settings.Settings;
|
|
import org.elasticsearch.common.settings.Settings;
|
|
import org.elasticsearch.common.unit.TimeValue;
|
|
import org.elasticsearch.common.unit.TimeValue;
|
|
import org.elasticsearch.common.util.concurrent.EsExecutors;
|
|
import org.elasticsearch.common.util.concurrent.EsExecutors;
|
|
@@ -31,7 +32,9 @@ import org.mockito.ArgumentCaptor;
|
|
|
|
|
|
import java.io.IOException;
|
|
import java.io.IOException;
|
|
import java.io.UncheckedIOException;
|
|
import java.io.UncheckedIOException;
|
|
|
|
+import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
|
+import java.util.Map;
|
|
import java.util.concurrent.BrokenBarrierException;
|
|
import java.util.concurrent.BrokenBarrierException;
|
|
import java.util.concurrent.CopyOnWriteArrayList;
|
|
import java.util.concurrent.CopyOnWriteArrayList;
|
|
import java.util.concurrent.CountDownLatch;
|
|
import java.util.concurrent.CountDownLatch;
|
|
@@ -76,62 +79,70 @@ public class GlobalCheckpointListenersTests extends ESTestCase {
|
|
final GlobalCheckpointListeners globalCheckpointListeners =
|
|
final GlobalCheckpointListeners globalCheckpointListeners =
|
|
new GlobalCheckpointListeners(shardId, Runnable::run, scheduler, logger);
|
|
new GlobalCheckpointListeners(shardId, Runnable::run, scheduler, logger);
|
|
globalCheckpointListeners.globalCheckpointUpdated(NO_OPS_PERFORMED);
|
|
globalCheckpointListeners.globalCheckpointUpdated(NO_OPS_PERFORMED);
|
|
- final int numberOfListeners = randomIntBetween(0, 16);
|
|
|
|
- final long[] globalCheckpoints = new long[numberOfListeners];
|
|
|
|
|
|
+ final int numberOfListeners = randomIntBetween(0, 64);
|
|
|
|
+ final Map<GlobalCheckpointListeners.GlobalCheckpointListener, Long> listeners = new HashMap<>();
|
|
|
|
+ final Map<GlobalCheckpointListeners.GlobalCheckpointListener, Long> notifiedListeners = new HashMap<>();
|
|
for (int i = 0; i < numberOfListeners; i++) {
|
|
for (int i = 0; i < numberOfListeners; i++) {
|
|
- final int index = i;
|
|
|
|
- final AtomicBoolean invoked = new AtomicBoolean();
|
|
|
|
- final GlobalCheckpointListeners.GlobalCheckpointListener listener =
|
|
|
|
- (g, e) -> {
|
|
|
|
- if (invoked.compareAndSet(false, true) == false) {
|
|
|
|
- throw new IllegalStateException("listener invoked twice");
|
|
|
|
- }
|
|
|
|
- assert g != UNASSIGNED_SEQ_NO;
|
|
|
|
- assert e == null;
|
|
|
|
- globalCheckpoints[index] = g;
|
|
|
|
- };
|
|
|
|
- globalCheckpointListeners.add(NO_OPS_PERFORMED, listener, null);
|
|
|
|
|
|
+ final GlobalCheckpointListeners.GlobalCheckpointListener listener = new GlobalCheckpointListeners.GlobalCheckpointListener() {
|
|
|
|
+ @Override
|
|
|
|
+ public void accept(final long g, final Exception e) {
|
|
|
|
+ notifiedListeners.put(this, g);
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ final long waitingGlobalCheckpoint = randomLongBetween(NO_OPS_PERFORMED, Long.MAX_VALUE);
|
|
|
|
+ listeners.put(listener, waitingGlobalCheckpoint);
|
|
|
|
+ globalCheckpointListeners.add(waitingGlobalCheckpoint, maybeMultipleInvocationProtectingListener(listener), null);
|
|
}
|
|
}
|
|
- final long globalCheckpoint = randomLongBetween(NO_OPS_PERFORMED, Long.MAX_VALUE);
|
|
|
|
|
|
+ final long globalCheckpoint = randomLongBetween(NO_OPS_PERFORMED, Long.MAX_VALUE - 1);
|
|
globalCheckpointListeners.globalCheckpointUpdated(globalCheckpoint);
|
|
globalCheckpointListeners.globalCheckpointUpdated(globalCheckpoint);
|
|
- for (int i = 0; i < numberOfListeners; i++) {
|
|
|
|
- assertThat(globalCheckpoints[i], equalTo(globalCheckpoint));
|
|
|
|
|
|
+ for (final Map.Entry<GlobalCheckpointListeners.GlobalCheckpointListener, Long> listener : listeners.entrySet()) {
|
|
|
|
+ if (listener.getValue() <= globalCheckpoint) {
|
|
|
|
+ // only listeners waiting on a lower global checkpoint will have been notified
|
|
|
|
+ assertThat(notifiedListeners.get(listener.getKey()), equalTo(globalCheckpoint));
|
|
|
|
+ } else {
|
|
|
|
+ assertNull(notifiedListeners.get(listener.getKey()));
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
// test the listeners are not invoked twice
|
|
// test the listeners are not invoked twice
|
|
- final long nextGlobalCheckpoint = randomLongBetween(globalCheckpoint + 1, Long.MAX_VALUE);
|
|
|
|
|
|
+ notifiedListeners.clear();
|
|
|
|
+ final long nextGlobalCheckpoint = randomLongBetween(1 + globalCheckpoint, Long.MAX_VALUE);
|
|
globalCheckpointListeners.globalCheckpointUpdated(nextGlobalCheckpoint);
|
|
globalCheckpointListeners.globalCheckpointUpdated(nextGlobalCheckpoint);
|
|
- for (int i = 0; i < numberOfListeners; i++) {
|
|
|
|
- assertThat(globalCheckpoints[i], equalTo(globalCheckpoint));
|
|
|
|
|
|
+ for (final Map.Entry<GlobalCheckpointListeners.GlobalCheckpointListener, Long> listener : listeners.entrySet()) {
|
|
|
|
+ if (listener.getValue() > globalCheckpoint && listener.getValue() <= nextGlobalCheckpoint) {
|
|
|
|
+ // these listeners will have been notified by the second global checkpoint update, and all the other listeners should not be
|
|
|
|
+ assertThat(notifiedListeners.get(listener.getKey()), equalTo(nextGlobalCheckpoint));
|
|
|
|
+ } else {
|
|
|
|
+ assertNull(notifiedListeners.get(listener.getKey()));
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
// closing should also not notify the listeners
|
|
// closing should also not notify the listeners
|
|
|
|
+ notifiedListeners.clear();
|
|
globalCheckpointListeners.close();
|
|
globalCheckpointListeners.close();
|
|
- for (int i = 0; i < numberOfListeners; i++) {
|
|
|
|
- assertThat(globalCheckpoints[i], equalTo(globalCheckpoint));
|
|
|
|
|
|
+ for (final Map.Entry<GlobalCheckpointListeners.GlobalCheckpointListener, Long> listener : listeners.entrySet()) {
|
|
|
|
+ if (listener.getValue() > nextGlobalCheckpoint) {
|
|
|
|
+ // these listeners should have been notified that we closed, and all the other listeners should not be
|
|
|
|
+ assertThat(notifiedListeners.get(listener.getKey()), equalTo(UNASSIGNED_SEQ_NO));
|
|
|
|
+ } else {
|
|
|
|
+ assertNull(notifiedListeners.get(listener.getKey()));
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public void testListenersReadyToBeNotified() throws IOException {
|
|
public void testListenersReadyToBeNotified() throws IOException {
|
|
final GlobalCheckpointListeners globalCheckpointListeners =
|
|
final GlobalCheckpointListeners globalCheckpointListeners =
|
|
new GlobalCheckpointListeners(shardId, Runnable::run, scheduler, logger);
|
|
new GlobalCheckpointListeners(shardId, Runnable::run, scheduler, logger);
|
|
- final long globalCheckpoint = randomLongBetween(NO_OPS_PERFORMED + 1, Long.MAX_VALUE);
|
|
|
|
|
|
+ final long globalCheckpoint = randomLongBetween(0, Long.MAX_VALUE);
|
|
globalCheckpointListeners.globalCheckpointUpdated(globalCheckpoint);
|
|
globalCheckpointListeners.globalCheckpointUpdated(globalCheckpoint);
|
|
final int numberOfListeners = randomIntBetween(0, 16);
|
|
final int numberOfListeners = randomIntBetween(0, 16);
|
|
final long[] globalCheckpoints = new long[numberOfListeners];
|
|
final long[] globalCheckpoints = new long[numberOfListeners];
|
|
for (int i = 0; i < numberOfListeners; i++) {
|
|
for (int i = 0; i < numberOfListeners; i++) {
|
|
final int index = i;
|
|
final int index = i;
|
|
- final AtomicBoolean invoked = new AtomicBoolean();
|
|
|
|
- final GlobalCheckpointListeners.GlobalCheckpointListener listener =
|
|
|
|
- (g, e) -> {
|
|
|
|
- if (invoked.compareAndSet(false, true) == false) {
|
|
|
|
- throw new IllegalStateException("listener invoked twice");
|
|
|
|
- }
|
|
|
|
- assert g != UNASSIGNED_SEQ_NO;
|
|
|
|
- assert e == null;
|
|
|
|
- globalCheckpoints[index] = g;
|
|
|
|
- };
|
|
|
|
- globalCheckpointListeners.add(randomLongBetween(NO_OPS_PERFORMED, globalCheckpoint - 1), listener, null);
|
|
|
|
|
|
+ globalCheckpointListeners.add(
|
|
|
|
+ randomLongBetween(0, globalCheckpoint),
|
|
|
|
+ maybeMultipleInvocationProtectingListener((g, e) -> globalCheckpoints[index] = g),
|
|
|
|
+ null);
|
|
// the listener should be notified immediately
|
|
// the listener should be notified immediately
|
|
assertThat(globalCheckpoints[index], equalTo(globalCheckpoint));
|
|
assertThat(globalCheckpoints[index], equalTo(globalCheckpoint));
|
|
}
|
|
}
|
|
@@ -161,18 +172,17 @@ public class GlobalCheckpointListenersTests extends ESTestCase {
|
|
for (int i = 0; i < numberOfListeners; i++) {
|
|
for (int i = 0; i < numberOfListeners; i++) {
|
|
final int index = i;
|
|
final int index = i;
|
|
final boolean failure = randomBoolean();
|
|
final boolean failure = randomBoolean();
|
|
- final GlobalCheckpointListeners.GlobalCheckpointListener listener =
|
|
|
|
- (g, e) -> {
|
|
|
|
- assert globalCheckpoint != UNASSIGNED_SEQ_NO;
|
|
|
|
- assert e == null;
|
|
|
|
|
|
+ globalCheckpointListeners.add(
|
|
|
|
+ randomLongBetween(NO_OPS_PERFORMED, globalCheckpoint - 1),
|
|
|
|
+ maybeMultipleInvocationProtectingListener((g, e) -> {
|
|
if (failure) {
|
|
if (failure) {
|
|
globalCheckpoints[index] = Long.MIN_VALUE;
|
|
globalCheckpoints[index] = Long.MIN_VALUE;
|
|
throw new RuntimeException("failure");
|
|
throw new RuntimeException("failure");
|
|
} else {
|
|
} else {
|
|
globalCheckpoints[index] = globalCheckpoint;
|
|
globalCheckpoints[index] = globalCheckpoint;
|
|
}
|
|
}
|
|
- };
|
|
|
|
- globalCheckpointListeners.add(randomLongBetween(NO_OPS_PERFORMED, globalCheckpoint - 1), listener, null);
|
|
|
|
|
|
+ }),
|
|
|
|
+ null);
|
|
// the listener should be notified immediately
|
|
// the listener should be notified immediately
|
|
if (failure) {
|
|
if (failure) {
|
|
assertThat(globalCheckpoints[i], equalTo(Long.MIN_VALUE));
|
|
assertThat(globalCheckpoints[i], equalTo(Long.MIN_VALUE));
|
|
@@ -202,17 +212,8 @@ public class GlobalCheckpointListenersTests extends ESTestCase {
|
|
final Exception[] exceptions = new Exception[numberOfListeners];
|
|
final Exception[] exceptions = new Exception[numberOfListeners];
|
|
for (int i = 0; i < numberOfListeners; i++) {
|
|
for (int i = 0; i < numberOfListeners; i++) {
|
|
final int index = i;
|
|
final int index = i;
|
|
- final AtomicBoolean invoked = new AtomicBoolean();
|
|
|
|
- final GlobalCheckpointListeners.GlobalCheckpointListener listener =
|
|
|
|
- (globalCheckpoint, e) -> {
|
|
|
|
- if (invoked.compareAndSet(false, true) == false) {
|
|
|
|
- throw new IllegalStateException("listener invoked twice");
|
|
|
|
- }
|
|
|
|
- assert globalCheckpoint == UNASSIGNED_SEQ_NO;
|
|
|
|
- assert e != null;
|
|
|
|
- exceptions[index] = e;
|
|
|
|
- };
|
|
|
|
- globalCheckpointListeners.add(NO_OPS_PERFORMED, listener, null);
|
|
|
|
|
|
+ globalCheckpointListeners.add(
|
|
|
|
+ 0, maybeMultipleInvocationProtectingListener((g, e) -> exceptions[index] = e), null);
|
|
}
|
|
}
|
|
globalCheckpointListeners.close();
|
|
globalCheckpointListeners.close();
|
|
for (int i = 0; i < numberOfListeners; i++) {
|
|
for (int i = 0; i < numberOfListeners; i++) {
|
|
@@ -238,16 +239,13 @@ public class GlobalCheckpointListenersTests extends ESTestCase {
|
|
globalCheckpointListeners.close();
|
|
globalCheckpointListeners.close();
|
|
final AtomicBoolean invoked = new AtomicBoolean();
|
|
final AtomicBoolean invoked = new AtomicBoolean();
|
|
final CountDownLatch latch = new CountDownLatch(1);
|
|
final CountDownLatch latch = new CountDownLatch(1);
|
|
- final GlobalCheckpointListeners.GlobalCheckpointListener listener = (g, e) -> {
|
|
|
|
- assert g == UNASSIGNED_SEQ_NO;
|
|
|
|
- assert e != null;
|
|
|
|
- if (invoked.compareAndSet(false, true) == false) {
|
|
|
|
- latch.countDown();
|
|
|
|
- throw new IllegalStateException("listener invoked twice");
|
|
|
|
- }
|
|
|
|
- latch.countDown();
|
|
|
|
- };
|
|
|
|
- globalCheckpointListeners.add(randomLongBetween(NO_OPS_PERFORMED, Long.MAX_VALUE), listener, null);
|
|
|
|
|
|
+ globalCheckpointListeners.add(
|
|
|
|
+ randomLongBetween(NO_OPS_PERFORMED, Long.MAX_VALUE),
|
|
|
|
+ maybeMultipleInvocationProtectingListener((g, e) -> {
|
|
|
|
+ invoked.set(true);
|
|
|
|
+ latch.countDown();
|
|
|
|
+ }),
|
|
|
|
+ null);
|
|
latch.await();
|
|
latch.await();
|
|
assertTrue(invoked.get());
|
|
assertTrue(invoked.get());
|
|
}
|
|
}
|
|
@@ -264,18 +262,17 @@ public class GlobalCheckpointListenersTests extends ESTestCase {
|
|
final int index = i;
|
|
final int index = i;
|
|
final boolean failure = randomBoolean();
|
|
final boolean failure = randomBoolean();
|
|
failures[index] = failure;
|
|
failures[index] = failure;
|
|
- final GlobalCheckpointListeners.GlobalCheckpointListener listener =
|
|
|
|
- (g, e) -> {
|
|
|
|
- assert g != UNASSIGNED_SEQ_NO;
|
|
|
|
- assert e == null;
|
|
|
|
|
|
+ globalCheckpointListeners.add(
|
|
|
|
+ 0,
|
|
|
|
+ maybeMultipleInvocationProtectingListener((g, e) -> {
|
|
if (failure) {
|
|
if (failure) {
|
|
globalCheckpoints[index] = Long.MIN_VALUE;
|
|
globalCheckpoints[index] = Long.MIN_VALUE;
|
|
throw new RuntimeException("failure");
|
|
throw new RuntimeException("failure");
|
|
} else {
|
|
} else {
|
|
globalCheckpoints[index] = g;
|
|
globalCheckpoints[index] = g;
|
|
}
|
|
}
|
|
- };
|
|
|
|
- globalCheckpointListeners.add(NO_OPS_PERFORMED, listener, null);
|
|
|
|
|
|
+ }),
|
|
|
|
+ null);
|
|
}
|
|
}
|
|
final long globalCheckpoint = randomLongBetween(NO_OPS_PERFORMED, Long.MAX_VALUE);
|
|
final long globalCheckpoint = randomLongBetween(NO_OPS_PERFORMED, Long.MAX_VALUE);
|
|
globalCheckpointListeners.globalCheckpointUpdated(globalCheckpoint);
|
|
globalCheckpointListeners.globalCheckpointUpdated(globalCheckpoint);
|
|
@@ -319,17 +316,16 @@ public class GlobalCheckpointListenersTests extends ESTestCase {
|
|
final int index = i;
|
|
final int index = i;
|
|
final boolean failure = randomBoolean();
|
|
final boolean failure = randomBoolean();
|
|
failures[index] = failure;
|
|
failures[index] = failure;
|
|
- final GlobalCheckpointListeners.GlobalCheckpointListener listener =
|
|
|
|
- (g, e) -> {
|
|
|
|
- assert g == UNASSIGNED_SEQ_NO;
|
|
|
|
- assert e != null;
|
|
|
|
|
|
+ globalCheckpointListeners.add(
|
|
|
|
+ 0,
|
|
|
|
+ maybeMultipleInvocationProtectingListener((g, e) -> {
|
|
if (failure) {
|
|
if (failure) {
|
|
throw new RuntimeException("failure");
|
|
throw new RuntimeException("failure");
|
|
} else {
|
|
} else {
|
|
exceptions[index] = e;
|
|
exceptions[index] = e;
|
|
}
|
|
}
|
|
- };
|
|
|
|
- globalCheckpointListeners.add(NO_OPS_PERFORMED, listener, null);
|
|
|
|
|
|
+ }),
|
|
|
|
+ null);
|
|
}
|
|
}
|
|
globalCheckpointListeners.close();
|
|
globalCheckpointListeners.close();
|
|
for (int i = 0; i < numberOfListeners; i++) {
|
|
for (int i = 0; i < numberOfListeners; i++) {
|
|
@@ -370,12 +366,12 @@ public class GlobalCheckpointListenersTests extends ESTestCase {
|
|
final int numberOfListeners = randomIntBetween(0, 16);
|
|
final int numberOfListeners = randomIntBetween(0, 16);
|
|
for (int i = 0; i < numberOfListeners; i++) {
|
|
for (int i = 0; i < numberOfListeners; i++) {
|
|
globalCheckpointListeners.add(
|
|
globalCheckpointListeners.add(
|
|
- NO_OPS_PERFORMED,
|
|
|
|
- (g, e) -> {
|
|
|
|
|
|
+ 0,
|
|
|
|
+ maybeMultipleInvocationProtectingListener((g, e) -> {
|
|
notified.incrementAndGet();
|
|
notified.incrementAndGet();
|
|
assertThat(g, equalTo(globalCheckpoint));
|
|
assertThat(g, equalTo(globalCheckpoint));
|
|
assertNull(e);
|
|
assertNull(e);
|
|
- },
|
|
|
|
|
|
+ }),
|
|
null);
|
|
null);
|
|
}
|
|
}
|
|
globalCheckpointListeners.globalCheckpointUpdated(globalCheckpoint);
|
|
globalCheckpointListeners.globalCheckpointUpdated(globalCheckpoint);
|
|
@@ -396,13 +392,13 @@ public class GlobalCheckpointListenersTests extends ESTestCase {
|
|
for (int i = 0; i < numberOfListeners; i++) {
|
|
for (int i = 0; i < numberOfListeners; i++) {
|
|
globalCheckpointListeners.add(
|
|
globalCheckpointListeners.add(
|
|
NO_OPS_PERFORMED,
|
|
NO_OPS_PERFORMED,
|
|
- (g, e) -> {
|
|
|
|
|
|
+ maybeMultipleInvocationProtectingListener((g, e) -> {
|
|
notified.incrementAndGet();
|
|
notified.incrementAndGet();
|
|
assertThat(g, equalTo(UNASSIGNED_SEQ_NO));
|
|
assertThat(g, equalTo(UNASSIGNED_SEQ_NO));
|
|
assertNotNull(e);
|
|
assertNotNull(e);
|
|
assertThat(e, instanceOf(IndexShardClosedException.class));
|
|
assertThat(e, instanceOf(IndexShardClosedException.class));
|
|
assertThat(((IndexShardClosedException) e).getShardId(), equalTo(shardId));
|
|
assertThat(((IndexShardClosedException) e).getShardId(), equalTo(shardId));
|
|
- },
|
|
|
|
|
|
+ }),
|
|
null);
|
|
null);
|
|
}
|
|
}
|
|
assertThat(notified.get(), equalTo(numberOfListeners));
|
|
assertThat(notified.get(), equalTo(numberOfListeners));
|
|
@@ -423,11 +419,12 @@ public class GlobalCheckpointListenersTests extends ESTestCase {
|
|
for (int i = 0; i < numberOfListeners; i++) {
|
|
for (int i = 0; i < numberOfListeners; i++) {
|
|
globalCheckpointListeners.add(
|
|
globalCheckpointListeners.add(
|
|
randomLongBetween(0, globalCheckpoint),
|
|
randomLongBetween(0, globalCheckpoint),
|
|
- (g, e) -> {
|
|
|
|
|
|
+ maybeMultipleInvocationProtectingListener((g, e) -> {
|
|
notified.incrementAndGet();
|
|
notified.incrementAndGet();
|
|
assertThat(g, equalTo(globalCheckpoint));
|
|
assertThat(g, equalTo(globalCheckpoint));
|
|
assertNull(e);
|
|
assertNull(e);
|
|
- }, null);
|
|
|
|
|
|
+ }),
|
|
|
|
+ null);
|
|
}
|
|
}
|
|
assertThat(notified.get(), equalTo(numberOfListeners));
|
|
assertThat(notified.get(), equalTo(numberOfListeners));
|
|
assertThat(count.get(), equalTo(numberOfListeners));
|
|
assertThat(count.get(), equalTo(numberOfListeners));
|
|
@@ -472,11 +469,11 @@ public class GlobalCheckpointListenersTests extends ESTestCase {
|
|
// sometimes this will notify the listener immediately
|
|
// sometimes this will notify the listener immediately
|
|
globalCheckpointListeners.add(
|
|
globalCheckpointListeners.add(
|
|
globalCheckpoint.get(),
|
|
globalCheckpoint.get(),
|
|
- (g, e) -> {
|
|
|
|
|
|
+ maybeMultipleInvocationProtectingListener((g, e) -> {
|
|
if (invocation.compareAndSet(false, true) == false) {
|
|
if (invocation.compareAndSet(false, true) == false) {
|
|
throw new IllegalStateException("listener invoked twice");
|
|
throw new IllegalStateException("listener invoked twice");
|
|
}
|
|
}
|
|
- },
|
|
|
|
|
|
+ }),
|
|
randomBoolean() ? null : TimeValue.timeValueNanos(randomLongBetween(1, TimeUnit.MICROSECONDS.toNanos(1))));
|
|
randomBoolean() ? null : TimeValue.timeValueNanos(randomLongBetween(1, TimeUnit.MICROSECONDS.toNanos(1))));
|
|
}
|
|
}
|
|
// synchronize ending with the updating thread and the main test thread
|
|
// synchronize ending with the updating thread and the main test thread
|
|
@@ -511,7 +508,7 @@ public class GlobalCheckpointListenersTests extends ESTestCase {
|
|
final CountDownLatch latch = new CountDownLatch(1);
|
|
final CountDownLatch latch = new CountDownLatch(1);
|
|
globalCheckpointListeners.add(
|
|
globalCheckpointListeners.add(
|
|
NO_OPS_PERFORMED,
|
|
NO_OPS_PERFORMED,
|
|
- (g, e) -> {
|
|
|
|
|
|
+ maybeMultipleInvocationProtectingListener((g, e) -> {
|
|
try {
|
|
try {
|
|
notified.set(true);
|
|
notified.set(true);
|
|
assertThat(g, equalTo(UNASSIGNED_SEQ_NO));
|
|
assertThat(g, equalTo(UNASSIGNED_SEQ_NO));
|
|
@@ -527,7 +524,7 @@ public class GlobalCheckpointListenersTests extends ESTestCase {
|
|
} finally {
|
|
} finally {
|
|
latch.countDown();
|
|
latch.countDown();
|
|
}
|
|
}
|
|
- },
|
|
|
|
|
|
+ }),
|
|
timeout);
|
|
timeout);
|
|
latch.await();
|
|
latch.await();
|
|
|
|
|
|
@@ -546,7 +543,7 @@ public class GlobalCheckpointListenersTests extends ESTestCase {
|
|
final CountDownLatch latch = new CountDownLatch(1);
|
|
final CountDownLatch latch = new CountDownLatch(1);
|
|
globalCheckpointListeners.add(
|
|
globalCheckpointListeners.add(
|
|
NO_OPS_PERFORMED,
|
|
NO_OPS_PERFORMED,
|
|
- (g, e) -> {
|
|
|
|
|
|
+ maybeMultipleInvocationProtectingListener((g, e) -> {
|
|
try {
|
|
try {
|
|
notified.set(true);
|
|
notified.set(true);
|
|
assertThat(g, equalTo(UNASSIGNED_SEQ_NO));
|
|
assertThat(g, equalTo(UNASSIGNED_SEQ_NO));
|
|
@@ -554,7 +551,7 @@ public class GlobalCheckpointListenersTests extends ESTestCase {
|
|
} finally {
|
|
} finally {
|
|
latch.countDown();
|
|
latch.countDown();
|
|
}
|
|
}
|
|
- },
|
|
|
|
|
|
+ }),
|
|
timeout);
|
|
timeout);
|
|
latch.await();
|
|
latch.await();
|
|
// ensure the listener notification occurred on the executor
|
|
// ensure the listener notification occurred on the executor
|
|
@@ -574,9 +571,9 @@ public class GlobalCheckpointListenersTests extends ESTestCase {
|
|
final TimeValue timeout = TimeValue.timeValueMillis(randomIntBetween(1, 50));
|
|
final TimeValue timeout = TimeValue.timeValueMillis(randomIntBetween(1, 50));
|
|
globalCheckpointListeners.add(
|
|
globalCheckpointListeners.add(
|
|
NO_OPS_PERFORMED,
|
|
NO_OPS_PERFORMED,
|
|
- (g, e) -> {
|
|
|
|
|
|
+ maybeMultipleInvocationProtectingListener((g, e) -> {
|
|
throw new RuntimeException("failure");
|
|
throw new RuntimeException("failure");
|
|
- },
|
|
|
|
|
|
+ }),
|
|
timeout);
|
|
timeout);
|
|
latch.await();
|
|
latch.await();
|
|
final ArgumentCaptor<String> message = ArgumentCaptor.forClass(String.class);
|
|
final ArgumentCaptor<String> message = ArgumentCaptor.forClass(String.class);
|
|
@@ -592,10 +589,11 @@ public class GlobalCheckpointListenersTests extends ESTestCase {
|
|
final GlobalCheckpointListeners globalCheckpointListeners =
|
|
final GlobalCheckpointListeners globalCheckpointListeners =
|
|
new GlobalCheckpointListeners(shardId, Runnable::run, scheduler, logger);
|
|
new GlobalCheckpointListeners(shardId, Runnable::run, scheduler, logger);
|
|
final TimeValue timeout = TimeValue.timeValueNanos(Long.MAX_VALUE);
|
|
final TimeValue timeout = TimeValue.timeValueNanos(Long.MAX_VALUE);
|
|
- final GlobalCheckpointListeners.GlobalCheckpointListener globalCheckpointListener = (g, e) -> {
|
|
|
|
- assertThat(g, equalTo(NO_OPS_PERFORMED));
|
|
|
|
- assertNull(e);
|
|
|
|
- };
|
|
|
|
|
|
+ final GlobalCheckpointListeners.GlobalCheckpointListener globalCheckpointListener =
|
|
|
|
+ maybeMultipleInvocationProtectingListener((g, e) -> {
|
|
|
|
+ assertThat(g, equalTo(NO_OPS_PERFORMED));
|
|
|
|
+ assertNull(e);
|
|
|
|
+ });
|
|
globalCheckpointListeners.add(NO_OPS_PERFORMED, globalCheckpointListener, timeout);
|
|
globalCheckpointListeners.add(NO_OPS_PERFORMED, globalCheckpointListener, timeout);
|
|
final ScheduledFuture<?> future = globalCheckpointListeners.getTimeoutFuture(globalCheckpointListener);
|
|
final ScheduledFuture<?> future = globalCheckpointListeners.getTimeoutFuture(globalCheckpointListener);
|
|
assertNotNull(future);
|
|
assertNotNull(future);
|
|
@@ -603,6 +601,21 @@ public class GlobalCheckpointListenersTests extends ESTestCase {
|
|
assertTrue(future.isCancelled());
|
|
assertTrue(future.isCancelled());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ private GlobalCheckpointListeners.GlobalCheckpointListener maybeMultipleInvocationProtectingListener(
|
|
|
|
+ final GlobalCheckpointListeners.GlobalCheckpointListener globalCheckpointListener) {
|
|
|
|
+ if (Assertions.ENABLED) {
|
|
|
|
+ final AtomicBoolean invoked = new AtomicBoolean();
|
|
|
|
+ return (g, e) -> {
|
|
|
|
+ if (invoked.compareAndSet(false, true) == false) {
|
|
|
|
+ throw new AssertionError("listener invoked twice");
|
|
|
|
+ }
|
|
|
|
+ globalCheckpointListener.accept(g, e);
|
|
|
|
+ };
|
|
|
|
+ } else {
|
|
|
|
+ return globalCheckpointListener;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
private void awaitQuietly(final CyclicBarrier barrier) {
|
|
private void awaitQuietly(final CyclicBarrier barrier) {
|
|
try {
|
|
try {
|
|
barrier.await();
|
|
barrier.await();
|