@Test public void canMarkVariousOutcomes() { Map<Integer, Runnable> tasks = ImmutableMap.<Integer, Runnable>builderWithExpectedSize(6) .put(1, transactionOutcomeMetrics::markAbort) .put(2, transactionOutcomeMetrics::markSuccessfulCommit) .put(3, transactionOutcomeMetrics::markLocksExpired) .put(4, transactionOutcomeMetrics::markPutUnlessExistsFailed) .put(5, transactionOutcomeMetrics::markRollbackOtherTransaction) .put(6, transactionOutcomeMetrics::markPreCommitCheckFailed) .build(); tasks.entrySet().forEach(entry -> IntStream.range(0, entry.getKey()).forEach(unused -> entry.getValue().run())); assertThat(transactionOutcomeMetrics) .hasAborts(1) .hasSuccessfulCommits(2) .hasLocksExpired(3) .hasPutUnlessExistsFailures(4) .hasRollbackOther(5) .hasPreCommitCheckFailures(6); }
@Test public void conflictsInUnsafeTablesAreTrackedWithPlaceholder() { transactionOutcomeMetrics.markWriteWriteConflict(UNSAFE_REFERENCE_1); assertThat(transactionOutcomeMetrics).hasPlaceholderWriteWriteConflicts(1); }
@Test public void canMarkOneSuccessfulCommit() { transactionOutcomeMetrics.markSuccessfulCommit(); assertThat(transactionOutcomeMetrics).hasSuccessfulCommits(1); }
@Test public void readTransactionSucceedsIfConditionSucceeds() { serializableTxManager.runTaskWithConditionReadOnly(PreCommitConditions.NO_OP, (tx, condition) -> tx.get(TABLE, ImmutableSet.of(TEST_CELL))); TransactionOutcomeMetricsAssert.assertThat(transactionOutcomeMetrics) .hasSuccessfulCommits(1); }
@Test public void transactionDeletesAsyncOnRollback() { DeterministicScheduler executor = new DeterministicScheduler(); TestTransactionManager deleteTxManager = new TestTransactionManagerImpl( metricsManager, keyValueService, timestampService, timestampService, lockClient, lockService, transactionService, conflictDetectionManager, sweepStrategyManager, sweepQueue, executor); Supplier<PreCommitCondition> conditionSupplier = mock(Supplier.class); when(conditionSupplier.get()).thenReturn(ALWAYS_FAILS_CONDITION) .thenReturn(PreCommitConditions.NO_OP); deleteTxManager.runTaskWithConditionWithRetry(conditionSupplier, (tx, condition) -> { tx.put(TABLE, ImmutableMap.of(TEST_CELL, PtBytes.toBytes("value"))); return null; }); verify(keyValueService, times(0)).delete(any(), any()); executor.runUntilIdle(); verify(keyValueService, times(1)).delete(any(), any()); TransactionOutcomeMetricsAssert.assertThat(transactionOutcomeMetrics) .hasSuccessfulCommits(1) .hasFailedCommits(1); }
@Test public void readTransactionFailsIfConditionFails() { try { serializableTxManager.runTaskWithConditionReadOnly(ALWAYS_FAILS_CONDITION, (tx, condition) -> tx.get(TABLE, ImmutableSet.of(TEST_CELL))); fail(); } catch (TransactionFailedRetriableException e) { assertThat(e.getMessage(), containsString("Condition failed")); } TransactionOutcomeMetricsAssert.assertThat(transactionOutcomeMetrics) .hasFailedCommits(1); }
@Test public void conflictsInUnsafeTablesAreNotIncludedAsTags() { transactionOutcomeMetrics.markWriteWriteConflict(UNSAFE_REFERENCE_1); transactionOutcomeMetrics.markReadWriteConflict(UNSAFE_REFERENCE_2); assertThat(transactionOutcomeMetrics) .hasNoKnowledgeOf(UNSAFE_REFERENCE_1) .hasNoKnowledgeOf(UNSAFE_REFERENCE_2); }
@Test public void conflictsInDifferentTablesAreSeparateMetrics() { transactionOutcomeMetrics.markReadWriteConflict(SAFE_REFERENCE_1); transactionOutcomeMetrics.markReadWriteConflict(SAFE_REFERENCE_2); assertThat(transactionOutcomeMetrics) .hasNamedReadWriteConflicts(SAFE_REFERENCE_1, 1) .hasNamedReadWriteConflicts(SAFE_REFERENCE_2, 1); }
@Test public void tableReferencesIncludedAsTagIfSafe() { transactionOutcomeMetrics.markReadWriteConflict(SAFE_REFERENCE_1); transactionOutcomeMetrics.markReadWriteConflict(SAFE_REFERENCE_1); transactionOutcomeMetrics.markWriteWriteConflict(SAFE_REFERENCE_1); assertThat(transactionOutcomeMetrics) .hasNamedReadWriteConflicts(SAFE_REFERENCE_1, 2) .hasNamedWriteWriteConflicts(SAFE_REFERENCE_1, 1); }
@Test public void conflictsAcrossMultipleUnsafeTablesAreTrackedWithTheSamePlaceholder() { transactionOutcomeMetrics.markReadWriteConflict(UNSAFE_REFERENCE_1); transactionOutcomeMetrics.markReadWriteConflict(UNSAFE_REFERENCE_2); transactionOutcomeMetrics.markReadWriteConflict(UNSAFE_REFERENCE_1); transactionOutcomeMetrics.markReadWriteConflict(UNSAFE_REFERENCE_2); assertThat(transactionOutcomeMetrics).hasPlaceholderReadWriteConflicts(4); }
@Test public void canMarkMultipleSuccessfulCommits() { transactionOutcomeMetrics.markSuccessfulCommit(); transactionOutcomeMetrics.markSuccessfulCommit(); transactionOutcomeMetrics.markSuccessfulCommit(); transactionOutcomeMetrics.markSuccessfulCommit(); assertThat(transactionOutcomeMetrics).hasSuccessfulCommits(4); }
@Test public void testConcurrentWriteWriteConflicts() throws InterruptedException, ExecutionException { long val = concurrentlyIncrementValueThousandTimesAndGet(); assertEquals(1000, val); TransactionOutcomeMetricsAssert.assertThat(transactionOutcomeMetrics) .hasPlaceholderWriteWriteConflictsSatisfying(conflicts -> assertThat(conflicts, greaterThanOrEqualTo(1L))); }
@Test public void commitThrowsIfRolledBackAtCommitTime_expiredLocks() { final Cell cell = Cell.create(PtBytes.toBytes("row1"), PtBytes.toBytes("column1")); TimelockService timelockService = spy(new LegacyTimelockService(timestampService, lockService, lockClient)); // expire the locks when the pre-commit check happens - this is guaranteed to be after we've written the data PreCommitCondition condition = unused -> doReturn(ImmutableSet.of()).when(timelockService).refreshLockLeases(any()); LockImmutableTimestampResponse res = timelockService.lockImmutableTimestamp(IdentifiedTimeLockRequest.create()); long transactionTs = timelockService.getFreshTimestamp(); SnapshotTransaction snapshot = getSnapshotTransactionWith( timelockService, () -> transactionTs, res, condition); //simulate roll back at commit time transactionService.putUnlessExists(snapshot.getTimestamp(), TransactionConstants.FAILED_COMMIT_TS); snapshot.put(TABLE, ImmutableMap.of(cell, PtBytes.toBytes("value"))); assertThatExceptionOfType(TransactionLockTimeoutException.class).isThrownBy(snapshot::commit); timelockService.unlock(ImmutableSet.of(res.getLock())); TransactionOutcomeMetricsAssert.assertThat(transactionOutcomeMetrics) .hasFailedCommits(1) .hasLocksExpired(1); }
@Test public void correctlyDistinguishesConflictsInSafeAndUnsafeTables() { transactionOutcomeMetrics.markReadWriteConflict(UNSAFE_REFERENCE_1); transactionOutcomeMetrics.markReadWriteConflict(SAFE_REFERENCE_1); transactionOutcomeMetrics.markReadWriteConflict(UNSAFE_REFERENCE_2); transactionOutcomeMetrics.markReadWriteConflict(SAFE_REFERENCE_2); assertThat(transactionOutcomeMetrics) .hasPlaceholderReadWriteConflicts(2) .hasNamedReadWriteConflicts(SAFE_REFERENCE_1, 1) .hasNamedReadWriteConflicts(SAFE_REFERENCE_2, 1); } }