/** * A version of {@link TimelockService#unlock(Set)} where one does not need to know whether the locks associated * with the provided tokens were successfully unlocked or not. * * In some implementations, this may be more performant than a standard unlock. * * @param tokens Tokens for which associated locks should be unlocked. */ default void tryUnlock(Set<LockToken> tokens) { unlock(tokens); }
@Override public Set<LockToken> unlock(Set<LockToken> lockTokenV2s) throws InterruptedException { return timelockService.unlock(lockTokenV2s); }
public void unlock() { timeLock.unlock(ImmutableSet.of(lockToken)); } }
@Override public Set<LockToken> unlock(Set<LockToken> tokens) { return executeWithRecord(() -> timelockService.unlock(tokens)); }
@Override protected void performOneCall() { LockToken token = timelock.lock(nextRequest()).getToken(); boolean wasUnlocked = timelock.unlock(ImmutableSet.of(token)).contains(token); Preconditions.checkState(wasUnlocked, "unlock returned false"); }
@Override public Set<LockToken> unlock(Set<LockToken> tokens) { lockRefresher.unregisterLocks(tokens); return executeOnTimeLock(() -> delegate.unlock(tokens)); }
@Test public void clientWithSynchronousUnlockerDelegatesToUnlock() { try (TimeLockClient client = TimeLockClient.withSynchronousUnlocker(timelock)) { UUID uuid = UUID.randomUUID(); client.tryUnlock(ImmutableSet.of(LockToken.of(uuid))); verify(timelock, times(1)).unlock(ImmutableSet.of(LockToken.of(uuid))); } } }
@Test public void successfulLockAndUnlock() throws InterruptedException { LockToken lockToken = LockToken.of(UUID.randomUUID()); when(mockLockService.lock(any())) .thenReturn(() -> Optional.of(lockToken)); Optional<TargetedSweeperLock> maybeLock = TargetedSweeperLock .tryAcquire(1, TableMetadataPersistence.SweepStrategy.CONSERVATIVE, mockLockService); assertThat(maybeLock).isPresent(); TargetedSweeperLock lock = maybeLock.get(); assertThat(lock.getShardAndStrategy()).isEqualTo(ShardAndStrategy.conservative(1)); lock.unlock(); verify(mockLockService, times(1)).unlock(ImmutableSet.of(lockToken)); verify(mockLockService, times(1)).lock(any()); verifyNoMoreInteractions(mockLockService); }
@Test public void multipleSweepersSweepDifferentShardsAndCallUnlockAfterwards() throws InterruptedException { int shards = 128; int sweepers = 8; int threads = shards / sweepers; TimelockService stickyLockService = createStickyLockService(); createAndInitializeSweepersAndWaitForOneBackgroundIteration(sweepers, shards, threads, stickyLockService); for (int i = 0; i < shards; i++) { assertProgressUpdatedToTimestamp(maxTsForFinePartition(tsPartitionFine(unreadableTs - 1)), i); verify(stickyLockService, times(1)).unlock(ImmutableSet.of(LockToken.of(new UUID(i, 0L)))); } // minimum: all threads on one host succeed, then on another, etc: // threads + threads * 2 + ... + threads * swepers verify(stickyLockService, atLeast(threads * sweepers * (sweepers - 1) / 2)) .lock(any(LockRequest.class)); // maximum: all but one succeed on each host, and only then those succeed: // shards + shards - 1 + ... + shards - (sweepers - 1) verify(stickyLockService, atMost(sweepers * shards - sweepers * (sweepers - 1) / 2)) .lock(any(LockRequest.class)); }
/** * A version of {@link TimelockService#unlock(Set)} where one does not need to know whether the locks associated * with the provided tokens were successfully unlocked or not. * * In some implementations, this may be more performant than a standard unlock. * * @param tokens Tokens for which associated locks should be unlocked. */ default void tryUnlock(Set<LockToken> tokens) { unlock(tokens); }
@Test public void checkImmutableTsLockOnceIfThoroughlySwept_WithValidationOnReads() { TimelockService timelockService = spy(new LegacyTimelockService(timestampService, lockService, lockClient)); long transactionTs = timelockService.getFreshTimestamp(); LockImmutableTimestampResponse res = timelockService.lockImmutableTimestamp(IdentifiedTimeLockRequest.create()); SnapshotTransaction transaction = getSnapshotTransactionWith( timelockService, () -> transactionTs, res, PreCommitConditions.NO_OP, true); transaction.get(TABLE_SWEPT_THOROUGH, ImmutableSet.of(TEST_CELL)); transaction.commit(); timelockService.unlock(ImmutableSet.of(res.getLock())); verify(timelockService).refreshLockLeases(ImmutableSet.of(res.getLock())); }
@Test public void checkImmutableTsLockOnceIfThoroughlySwept_WithoutValidationOnReads() { TimelockService timelockService = spy(new LegacyTimelockService(timestampService, lockService, lockClient)); long transactionTs = timelockService.getFreshTimestamp(); LockImmutableTimestampResponse res = timelockService.lockImmutableTimestamp(IdentifiedTimeLockRequest.create()); SnapshotTransaction transaction = getSnapshotTransactionWith( timelockService, () -> transactionTs, res, PreCommitConditions.NO_OP, false); transaction.get(TABLE_SWEPT_THOROUGH, ImmutableSet.of(TEST_CELL)); transaction.commit(); timelockService.unlock(ImmutableSet.of(res.getLock())); verify(timelockService).refreshLockLeases(ImmutableSet.of(res.getLock())); }
public void unlock() { timeLock.unlock(ImmutableSet.of(lockToken)); } }
@Test public void validateLocksOnlyOnCommitIfValidationFlagIsFalse() { TimelockService timelockService = new LegacyTimelockService(timestampService, lockService, lockClient); long transactionTs = timelockService.getFreshTimestamp(); LockImmutableTimestampResponse res = timelockService.lockImmutableTimestamp(IdentifiedTimeLockRequest.create()); SnapshotTransaction transaction = getSnapshotTransactionWith( timelockService, () -> transactionTs, res, PreCommitConditions.NO_OP, false); timelockService.unlock(ImmutableSet.of(res.getLock())); transaction.get(TABLE_SWEPT_THOROUGH, ImmutableSet.of(TEST_CELL)); assertThatExceptionOfType(TransactionLockTimeoutException.class).isThrownBy(() -> transaction.commit()); }
@Test public void validateLocksOnReadsIfThoroughlySwept() { TimelockService timelockService = new LegacyTimelockService(timestampService, lockService, lockClient); long transactionTs = timelockService.getFreshTimestamp(); LockImmutableTimestampResponse res = timelockService.lockImmutableTimestamp(IdentifiedTimeLockRequest.create()); SnapshotTransaction transaction = getSnapshotTransactionWith( timelockService, () -> transactionTs, res, PreCommitConditions.NO_OP, true); timelockService.unlock(ImmutableSet.of(res.getLock())); assertThatExceptionOfType(TransactionLockTimeoutException.class).isThrownBy(() -> transaction.get(TABLE_SWEPT_THOROUGH, ImmutableSet.of(TEST_CELL))); }
@Test public void commitDoesNotThrowIfAlreadySuccessfullyCommitted() { final Cell cell = Cell.create(PtBytes.toBytes("row1"), PtBytes.toBytes("column1")); TimestampService timestampServiceSpy = spy(timestampService); TimelockService timelockService = new LegacyTimelockService(timestampServiceSpy, lockService, lockClient); long transactionTs = timelockService.getFreshTimestamp(); LockImmutableTimestampResponse res = timelockService.lockImmutableTimestamp(IdentifiedTimeLockRequest.create()); SnapshotTransaction snapshot = getSnapshotTransactionWith( timelockService, () -> transactionTs, res, PreCommitConditions.NO_OP); when(timestampServiceSpy.getFreshTimestamp()).thenReturn(10000000L); //forcing to try to commit a transaction that is already committed transactionService.putUnlessExists(transactionTs, timelockService.getFreshTimestamp()); snapshot.put(TABLE, ImmutableMap.of(cell, PtBytes.toBytes("value"))); snapshot.commit(); timelockService.unlock(Collections.singleton(res.getLock())); }
@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 commitThrowsIfRolledBackAtCommitTime_alreadyAborted() { final Cell cell = Cell.create(PtBytes.toBytes("row1"), PtBytes.toBytes("column1")); TimelockService timelockService = new LegacyTimelockService(timestampService, lockService, lockClient); LockImmutableTimestampResponse res = timelockService.lockImmutableTimestamp(IdentifiedTimeLockRequest.create()); long transactionTs = timelockService.getFreshTimestamp(); SnapshotTransaction snapshot = getSnapshotTransactionWith( timelockService, () -> transactionTs, res, PreCommitConditions.NO_OP); //forcing to try to commit a transaction that is already committed transactionService.putUnlessExists(transactionTs, TransactionConstants.FAILED_COMMIT_TS); snapshot.put(TABLE, ImmutableMap.of(cell, PtBytes.toBytes("value"))); assertThatExceptionOfType(TransactionCommitFailedException.class).isThrownBy(snapshot::commit); timelockService.unlock(Collections.singleton(res.getLock())); }
@Override public Set<LockToken> unlock(Set<LockToken> tokens) { lockRefresher.unregisterLocks(tokens); return executeOnTimeLock(() -> delegate.unlock(tokens)); }