use of com.github.rholder.retry.BlockStrategy in project gerrit by GerritCodeReview.
the class ExternalIdIT method retryOnLockFailure.
@Test
public void retryOnLockFailure() throws Exception {
Retryer<RefsMetaExternalIdsUpdate> retryer = ExternalIdsUpdate.retryerBuilder().withBlockStrategy(new BlockStrategy() {
@Override
public void block(long sleepTime) {
// Don't sleep in tests.
}
}).build();
ExternalId.Key fooId = ExternalId.Key.create("foo", "foo");
ExternalId.Key barId = ExternalId.Key.create("bar", "bar");
final AtomicBoolean doneBgUpdate = new AtomicBoolean(false);
ExternalIdsUpdate update = new ExternalIdsUpdate(repoManager, allUsers, metricMaker, externalIds, new DisabledExternalIdCache(), serverIdent.get(), serverIdent.get(), () -> {
if (!doneBgUpdate.getAndSet(true)) {
try {
extIdsUpdate.create().insert(ExternalId.create(barId, admin.id));
} catch (IOException | ConfigInvalidException | OrmException e) {
// Ignore, the successful insertion of the external ID is asserted later
}
}
}, retryer);
assertThat(doneBgUpdate.get()).isFalse();
update.insert(ExternalId.create(fooId, admin.id));
assertThat(doneBgUpdate.get()).isTrue();
assertThat(externalIds.get(fooId)).isNotNull();
assertThat(externalIds.get(barId)).isNotNull();
}
use of com.github.rholder.retry.BlockStrategy in project gerrit by GerritCodeReview.
the class RepoSequenceTest method idCanBeRetrievedFromOtherThreadWhileWaitingToRetry.
@Test
public void idCanBeRetrievedFromOtherThreadWhileWaitingToRetry() throws Exception {
// Seed existing ref value.
writeBlob("id", "1");
// Let the first update of the sequence fail with LOCK_FAILURE, so that the update is retried.
CountDownLatch lockFailure = new CountDownLatch(1);
CountDownLatch parallelSuccessfulSequenceGeneration = new CountDownLatch(1);
AtomicBoolean doneBgUpdate = new AtomicBoolean(false);
Runnable bgUpdate = () -> {
if (!doneBgUpdate.getAndSet(true)) {
writeBlob("id", "1234");
}
};
BlockStrategy blockStrategy = t -> {
// Keep blocking until we verified that another thread can retrieve a sequence number
// while we are blocking here.
lockFailure.countDown();
parallelSuccessfulSequenceGeneration.await();
};
// Use batch size = 1 to make each call go to NoteDb.
RepoSequence s = newSequence("id", 1, 1, bgUpdate, RepoSequence.retryerBuilder().withBlockStrategy(blockStrategy).build());
assertThat(doneBgUpdate.get()).isFalse();
// Start a thread to get a sequence number. This thread needs to update the sequence in NoteDb,
// but due to the background update (see bgUpdate) the first attempt to update NoteDb fails
// with LOCK_FAILURE. RepoSequence uses a retryer to retry the NoteDb update on LOCK_FAILURE,
// but our block strategy ensures that this retry only happens after isBlocking was set to
// false.
Future<?> future = Executors.newFixedThreadPool(1).submit(() -> {
// The background update sets the next available sequence number to 1234. Then the
// test thread retrieves one sequence number, so that the next available sequence
// number for this thread is 1235.
expect.that(s.next()).isEqualTo(1235);
});
// Wait until the LOCK_FAILURE has happened and the block strategy was entered.
lockFailure.await();
// Verify that the background update was done now.
assertThat(doneBgUpdate.get()).isTrue();
// Verify that we can retrieve a sequence number while the other thread is blocked. If the
// s.next() call hangs it means that the RepoSequence.counterLock was not released before the
// background thread started to block for retry. In this case the test would time out.
assertThat(s.next()).isEqualTo(1234);
// Stop blocking the retry of the background thread (and verify that it was still blocked).
parallelSuccessfulSequenceGeneration.countDown();
// Wait until the background thread is done.
future.get();
// Two successful acquire calls (because batch size == 1).
assertThat(s.acquireCount).isEqualTo(2);
}
Aggregations