Search in sources :

Example 1 with OutOfOrderSequence

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);
    }
}
Also used : AtomicLong(java.util.concurrent.atomic.AtomicLong) ArrayQueueOutOfOrderSequence(org.neo4j.util.concurrent.ArrayQueueOutOfOrderSequence) Race(org.neo4j.test.Race) ArrayQueueOutOfOrderSequence(org.neo4j.util.concurrent.ArrayQueueOutOfOrderSequence) OutOfOrderSequence(org.neo4j.util.concurrent.OutOfOrderSequence) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Test(org.junit.jupiter.api.Test)

Aggregations

ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)1 AtomicLong (java.util.concurrent.atomic.AtomicLong)1 Test (org.junit.jupiter.api.Test)1 Race (org.neo4j.test.Race)1 ArrayQueueOutOfOrderSequence (org.neo4j.util.concurrent.ArrayQueueOutOfOrderSequence)1 OutOfOrderSequence (org.neo4j.util.concurrent.OutOfOrderSequence)1