use of org.neo4j.util.concurrent.OutOfOrderSequence in project neo4j by neo4j.
the class GBPTreeGenericCountsStoreTest method shouldCheckpointAndRecoverConsistentlyUnderStressfulLoad.
@Test
void shouldCheckpointAndRecoverConsistentlyUnderStressfulLoad() throws Throwable {
// given
// it's good if it's way more than number of cores so that it creates some scheduling issues
int threads = 50;
int numberOfRounds = 5;
int roundTimeMillis = 300;
ConcurrentMap<CountsKey, AtomicLong> expected = new ConcurrentHashMap<>();
AtomicLong nextTxId = new AtomicLong(BASE_TX_ID);
AtomicLong lastCheckPointedTxId = new AtomicLong(nextTxId.longValue());
long lastRoundClosedAt = BASE_TX_ID;
// Start at some number > 0 so that we can do negative deltas now and then
long baseCount = 10_000;
try (CountUpdater initialApplier = countsStore.updater(nextTxId.incrementAndGet(), NULL)) {
for (int s = -1; s < HIGH_TOKEN_ID; s++) {
initialApplier.increment(nodeKey(s), baseCount);
for (int t = -1; t < HIGH_TOKEN_ID; t++) {
for (int e = -1; e < HIGH_TOKEN_ID; e++) {
initialApplier.increment(relationshipKey(s, t, e), baseCount);
}
}
}
}
OutOfOrderSequence lastClosedTxId = new ArrayQueueOutOfOrderSequence(nextTxId.get(), 200, EMPTY_LONG_ARRAY);
// when
for (int r = 0; r < numberOfRounds; r++) {
// Let loose updaters and a check-pointer
Race race = new Race().withMaxDuration(roundTimeMillis, TimeUnit.MILLISECONDS);
race.addContestants(threads, throwing(() -> {
long txId = nextTxId.incrementAndGet();
// Sleep a random time after getting the txId, this creates bigger temporary gaps in the txId sequence
Thread.sleep(ThreadLocalRandom.current().nextInt(5));
generateAndApplyTransaction(expected, txId);
lastClosedTxId.offer(txId, EMPTY_LONG_ARRAY);
}));
race.addContestant(throwing(() -> {
long checkpointTxId = lastClosedTxId.getHighestGapFreeNumber();
countsStore.checkpoint(NULL);
lastCheckPointedTxId.set(checkpointTxId);
Thread.sleep(ThreadLocalRandom.current().nextInt(roundTimeMillis / 5));
}));
race.go();
// Crash here, well not really crash but close the counts store knowing that there's any number of transactions since the last checkpoint
// and we know the last committed tx id as well as the (pessimistic) last check-pointed tx id.
crashAndRestartCountsStore();
recover(lastCheckPointedTxId.get(), nextTxId.get());
assertThat(nextTxId.get()).isGreaterThan(lastRoundClosedAt);
lastRoundClosedAt = nextTxId.get();
// then
assertCountsMatchesExpected(expected, baseCount);
}
}
Aggregations