@Test public void multipleInvalidatorsForSameKeyValueServiceAllReturnSameResults() { long limit = getBoundAfterTakingOutOneMillionTimestamps(); CassandraTestTools.executeInParallelOnExecutorService(() -> assertThat(CassandraTimestampStoreInvalidator.create(kv).backupAndInvalidate()).isEqualTo(limit)); }
/** * Consistent results here mean that: * (1) all calls to backupAndInvalidate() return the same value V, and * (2) this value V is the maximum of all successfully stored timestamp bounds. */ @Test public void invalidationDuringTimestampIssuanceYieldsConsistentResults() { Set<Long> backedUpValues = Sets.newConcurrentHashSet(); AtomicLong maxSuccessfulBound = new AtomicLong(CassandraTimestampUtils.INITIAL_VALUE); CassandraTestTools.executeInParallelOnExecutorService(() -> { CassandraTimestampStoreInvalidator storeInvalidator = CassandraTimestampStoreInvalidator.create(kv); try { if (ThreadLocalRandom.current().nextBoolean()) { backedUpValues.add(storeInvalidator.backupAndInvalidate()); } else { maxSuccessfulBound.accumulateAndGet(getBoundAfterTakingOutOneMillionTimestamps(), Math::max); } } catch (IllegalArgumentException | IllegalStateException | MultipleRunningTimestampServiceError error) { // Can arise if trying to manipulate the timestamp bound during/after an invalidation. This is fine. } }); if (!backedUpValues.isEmpty()) { invalidator.revalidateFromBackup(); assertThat(Iterables.getOnlyElement(backedUpValues)).isEqualTo(maxSuccessfulBound.get()); assertWeCanReadTimestamp(maxSuccessfulBound.get()); } // 2^-32 chance that nothing got backed up; accept in this case. }
@Test public void resilientToMultipleConcurrentBackups() { CassandraTestTools.executeInParallelOnExecutorService(backupRunner::backupExistingTimestamp); assertBoundNotReadable(); backupRunner.restoreFromBackup(); assertBoundEquals(INITIAL_VALUE); }
@Test public void resilientToMultipleConcurrentRestores() { backupRunner.backupExistingTimestamp(); CassandraTestTools.executeInParallelOnExecutorService(() -> { CassandraTimestampBackupRunner runner = new CassandraTimestampBackupRunner(kv); runner.restoreFromBackup(); }); assertBoundEquals(INITIAL_VALUE); }
@Test public void stateIsWellDefinedEvenUnderConcurrentBackupsAndRestores() { timestampBoundStore.getUpperLimit(); timestampBoundStore.storeUpperLimit(TIMESTAMP_3); CassandraTestTools.executeInParallelOnExecutorService(() -> { CassandraTimestampBackupRunner runner = new CassandraTimestampBackupRunner(kv); try { if (ThreadLocalRandom.current().nextBoolean()) { runner.backupExistingTimestamp(); } else { runner.restoreFromBackup(); } } catch (IllegalStateException exception) { // This is possible under concurrent backup *and* restore. // The point of this test is to ensure that the table is in a well defined state at the end. } }); backupRunner.restoreFromBackup(); assertThat(timestampBoundStore.getUpperLimit()).isEqualTo(TIMESTAMP_3); }