@VisibleForTesting HeldLocks(LockLog lockLog, Collection<AsyncLock> acquiredLocks, UUID requestId, LeaseExpirationTimer expirationTimer) { this.lockLog = lockLog; this.acquiredLocks = acquiredLocks; this.token = LockToken.of(requestId); this.expirationTimer = expirationTimer; }
public UUID getRequestId() { return token.getRequestId(); }
private LockToken mockHeldLocksForNewRequest(Consumer<HeldLocks> mockApplier) { LockToken request = LockToken.of(UUID.randomUUID()); HeldLocks heldLocks = mock(HeldLocks.class); mockApplier.accept(heldLocks); AsyncResult<HeldLocks> completedResult = new AsyncResult<>(); completedResult.complete(heldLocks); heldLocksCollection.getExistingOrAcquire(request.getRequestId(), () -> completedResult); return request; }
private LockToken mockFailedRequest() { LockToken request = LockToken.of(UUID.randomUUID()); AsyncResult failedLocks = new AsyncResult(); failedLocks.fail(new RuntimeException()); heldLocksCollection.getExistingOrAcquire(request.getRequestId(), () -> failedLocks); return request; }
private static LockToken randomLockToken() { return LockToken.of(UUID.randomUUID()); }
private LockToken mockTimedOutRequest() { LockToken request = LockToken.of(UUID.randomUUID()); AsyncResult timedOutResult = new AsyncResult(); timedOutResult.timeout(); heldLocksCollection.getExistingOrAcquire(request.getRequestId(), () -> timedOutResult); return request; }
public synchronized boolean unlock() { if (isUnlocked) { return false; } isUnlocked = true; for (AsyncLock lock : acquiredLocks) { lock.unlock(token.getRequestId()); } return true; }
static LockToken toTokenV2(LockRefreshToken legacyToken) { return LockToken.of(toUuid(legacyToken.getTokenId())); }
public Set<LockToken> unlock(Set<LockToken> tokens) { Set<LockToken> unlocked = filter(tokens, HeldLocks::unlock); for (LockToken token : unlocked) { heldLocksById.remove(token.getRequestId()); } return unlocked; }
private static List<LockToken> createLockTokenList(int size) { return IntStream.range(0, size) .boxed() .map(unused -> LockToken.of(UUID.randomUUID())) .collect(Collectors.toList()); }
private Set<LockToken> filter(Set<LockToken> tokens, Predicate<HeldLocks> predicate) { Set<LockToken> filtered = Sets.newHashSetWithExpectedSize(tokens.size()); for (LockToken token : tokens) { AsyncResult<HeldLocks> lockResult = heldLocksById.get(token.getRequestId()); if (lockResult != null && lockResult.test(predicate)) { filtered.add(token); } } return filtered; }
@Test public void profilingIsSharedAcrossTransactions() { SnapshotTransaction tx1 = snapshotTransactionManager.createTransaction(1, () -> 2L, LockToken.of( UUID.randomUUID()), PreCommitConditions.NO_OP); SnapshotTransaction tx2 = snapshotTransactionManager.createTransaction(1, () -> 2L, LockToken.of( UUID.randomUUID()), PreCommitConditions.NO_OP); assertThat(tx1.commitProfileProcessor).isSameAs(tx2.commitProfileProcessor); } }
static LockRefreshToken toLegacyToken(LockToken tokenV2) { return new LockRefreshToken(toBigInteger(tokenV2.getRequestId()), Long.MIN_VALUE); }
@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))); } } }
/** * Unlocks if expired, and returns whether the locks are now unlocked (regardless of whether or not they were * unlocked as a result of calling this method). */ public synchronized boolean unlockIfExpired() { if (expirationTimer.isExpired()) { if (unlock()) { lockLog.lockExpired(token.getRequestId(), getLockDescriptors()); } } return isUnlocked; }
/** * Creates a mock of a LockService that only gives out a lock once per unique request and never releases it, even * if unlock is called. The returned tokens are monotonically increasing in the tokenId. */ private TimelockService createStickyLockService() { AtomicLong lockToken = new AtomicLong(0); Set<LockDescriptor> requestedLocks = new ConcurrentHashSet<>(); TimelockService stickyLockService = mock(TimelockService.class); doAnswer(invocation -> { LockRequest request = invocation.getArgument(0); if (requestedLocks.add(Iterables.getOnlyElement(request.getLockDescriptors()))) { return (LockResponse) () -> Optional.of(LockToken.of(new UUID(lockToken.getAndIncrement(), 0L))); } else { return (LockResponse) Optional::empty; } }).when(stickyLockService).lock(any()); return stickyLockService; }
@Test public void removesExpiredAndFailedRequests() { UUID nonExpiredRequest = mockNonExpiredRequest().getRequestId(); mockExpiredRequest(); mockFailedRequest(); assertThat(heldLocksCollection.heldLocksById.size()).isEqualTo(3); heldLocksCollection.removeExpired(); assertThat(heldLocksCollection.heldLocksById.size()).isEqualTo(1); assertThat(heldLocksCollection.heldLocksById.keySet().iterator().next()).isEqualTo(nonExpiredRequest); }
@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); }
public UUID getRequestId() { return token.getRequestId(); }
@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)); }