@Override public void tryUnlock(Set<LockToken> tokens) { executeWithRecord(() -> { timelockService.tryUnlock(tokens); return null; }); }
public static AsyncTimeLockUnlocker create(TimelockService timelockService) { return new AsyncTimeLockUnlocker(DisruptorAutobatcher.create(batch -> { Set<LockToken> allTokensToUnlock = batch.stream() .map(BatchElement::argument) .flatMap(Collection::stream) .collect(Collectors.toSet()); try { timelockService.tryUnlock(allTokensToUnlock); } catch (Throwable t) { log.info("Failed to unlock lock tokens {} from timelock. They will eventually expire on their own, " + "but if this message recurs frequently, it may be worth investigation.", SafeArg.of("lockTokens", allTokensToUnlock), t); } batch.stream().map(BatchElement::result).forEach(f -> f.set(null)); })); }
@SuppressWarnings("unchecked") // Mock invocation known to be correct private void setupTokenCollectingTimeLock() { doAnswer(invocation -> { unlockedTokens.addAll((Set<LockToken>) invocation.getArguments()[0]); return null; }).when(timelockService).tryUnlock(any()); }
private void verifyTryUnlockAttemptedAtLeastOnce() { assertConditionEventuallyTrue(() -> { verify(timelockService, atLeastOnce()).tryUnlock(any()); verifyNoMoreInteractions(timelockService); }); }
@Test(timeout = 2_000) public void enqueueDoesNotBlock() { doAnswer(invocation -> { Uninterruptibles.sleepUninterruptibly(30, TimeUnit.SECONDS); return null; }).when(timelockService).tryUnlock(any()); unlocker.enqueue(ImmutableSet.copyOf(tokenList)); // If enqueue blocked till unlock completed, this test would fail after 2 seconds }
@Override public <T, E extends Exception> T finishRunTaskWithLockThrowOnConflict(TransactionAndImmutableTsLock txAndLock, TransactionTask<T, E> task) throws E, TransactionFailedRetriableException { Timer postTaskTimer = getTimer("finishTask"); Timer.Context postTaskContext; TransactionTask<T, E> wrappedTask = wrapTaskIfNecessary(task, txAndLock.immutableTsLock()); SnapshotTransaction tx = (SnapshotTransaction) txAndLock.transaction(); T result; try { result = runTaskThrowOnConflict(wrappedTask, tx); } finally { postTaskContext = postTaskTimer.time(); timelockService.tryUnlock(ImmutableSet.of(txAndLock.immutableTsLock())); } scrubForAggressiveHardDelete(tx); postTaskContext.stop(); return result; }
@SuppressWarnings("unchecked") // Mock invocation known to be correct @Test public void noParallelCallsMadeFromTimelockPointOfView() { AtomicBoolean timelockInUse = new AtomicBoolean(false); AtomicBoolean concurrentlyInvoked = new AtomicBoolean(false); doAnswer(invocation -> { if (timelockInUse.get()) { concurrentlyInvoked.set(true); } timelockInUse.set(true); unlockedTokens.addAll((Set<LockToken>) invocation.getArguments()[0]); timelockInUse.set(false); return null; }).when(timelockService).tryUnlock(any()); tokenList.forEach(token -> unlocker.enqueue(ImmutableSet.of(token))); verifyTryUnlockAttemptedAtLeastOnce(); assertAllTokensEventuallyUnlocked(); assertThat(concurrentlyInvoked.get()).as("TimeLock was, at some point, called concurrently").isFalse(); }
@Override public TransactionAndImmutableTsLock setupRunTaskWithConditionThrowOnConflict(PreCommitCondition condition) { StartIdentifiedAtlasDbTransactionResponse transactionResponse = timelockService.startIdentifiedAtlasDbTransaction( StartIdentifiedAtlasDbTransactionRequest.createForRequestor(clientId)); try { LockToken immutableTsLock = transactionResponse.immutableTimestamp().getLock(); long immutableTs = transactionResponse.immutableTimestamp().getImmutableTimestamp(); recordImmutableTimestamp(immutableTs); cleaner.punch(transactionResponse.startTimestampAndPartition().timestamp()); Supplier<Long> startTimestampSupplier = Suppliers.ofInstance( transactionResponse.startTimestampAndPartition().timestamp()); SnapshotTransaction transaction = createTransaction(immutableTs, startTimestampSupplier, immutableTsLock, condition); return TransactionAndImmutableTsLock.of(transaction, immutableTsLock); } catch (Throwable e) { timelockService.tryUnlock(ImmutableSet.of(transactionResponse.immutableTimestamp().getLock())); throw Throwables.rewrapAndThrowUncheckedException(e); } }
} finally { long microsForPostCommitUnlock = runAndReportTimeAndGetDurationMicros( () -> timelockService.tryUnlock(ImmutableSet.of(commitLocksToken)), "postCommitUnlock");
@Override public void tryUnlock(Set<LockToken> tokens) { executeWithRecord(() -> { timelockService.tryUnlock(tokens); return null; }); }
public static AsyncTimeLockUnlocker create(TimelockService timelockService) { return new AsyncTimeLockUnlocker(DisruptorAutobatcher.create(batch -> { Set<LockToken> allTokensToUnlock = batch.stream() .map(BatchElement::argument) .flatMap(Collection::stream) .collect(Collectors.toSet()); try { timelockService.tryUnlock(allTokensToUnlock); } catch (Throwable t) { log.info("Failed to unlock lock tokens {} from timelock. They will eventually expire on their own, " + "but if this message recurs frequently, it may be worth investigation.", SafeArg.of("lockTokens", allTokensToUnlock), t); } batch.stream().map(BatchElement::result).forEach(f -> f.set(null)); })); }
@Override public <T, E extends Exception> T finishRunTaskWithLockThrowOnConflict(TransactionAndImmutableTsLock txAndLock, TransactionTask<T, E> task) throws E, TransactionFailedRetriableException { Timer postTaskTimer = getTimer("finishTask"); Timer.Context postTaskContext; TransactionTask<T, E> wrappedTask = wrapTaskIfNecessary(task, txAndLock.immutableTsLock()); SnapshotTransaction tx = (SnapshotTransaction) txAndLock.transaction(); T result; try { result = runTaskThrowOnConflict(wrappedTask, tx); } finally { postTaskContext = postTaskTimer.time(); timelockService.tryUnlock(ImmutableSet.of(txAndLock.immutableTsLock())); } scrubForAggressiveHardDelete(tx); postTaskContext.stop(); return result; }
@Override public TransactionAndImmutableTsLock setupRunTaskWithConditionThrowOnConflict(PreCommitCondition condition) { StartIdentifiedAtlasDbTransactionResponse transactionResponse = timelockService.startIdentifiedAtlasDbTransaction( StartIdentifiedAtlasDbTransactionRequest.createForRequestor(clientId)); try { LockToken immutableTsLock = transactionResponse.immutableTimestamp().getLock(); long immutableTs = transactionResponse.immutableTimestamp().getImmutableTimestamp(); recordImmutableTimestamp(immutableTs); cleaner.punch(transactionResponse.startTimestampAndPartition().timestamp()); Supplier<Long> startTimestampSupplier = Suppliers.ofInstance( transactionResponse.startTimestampAndPartition().timestamp()); SnapshotTransaction transaction = createTransaction(immutableTs, startTimestampSupplier, immutableTsLock, condition); return TransactionAndImmutableTsLock.of(transaction, immutableTsLock); } catch (Throwable e) { timelockService.tryUnlock(ImmutableSet.of(transactionResponse.immutableTimestamp().getLock())); throw Throwables.rewrapAndThrowUncheckedException(e); } }
} finally { long microsForPostCommitUnlock = runAndReportTimeAndGetDurationMicros( () -> timelockService.tryUnlock(ImmutableSet.of(commitLocksToken)), "postCommitUnlock");