/** * Creates a new CompletableFuture that will timeout after the given amount of time. * * @param timeout The timeout for the future. * @param executorService An ExecutorService that will be used to invoke the timeout on. * @param <T> The Type argument for the CompletableFuture to create. * @return A CompletableFuture with a timeout. */ public static <T> CompletableFuture<T> futureWithTimeout(Duration timeout, ScheduledExecutorService executorService) { return futureWithTimeout(timeout, null, executorService); }
TailRead(long afterSequenceNumber, int maxCount, Duration timeout, ScheduledExecutorService executor) { this.afterSequenceNumber = afterSequenceNumber; this.maxCount = maxCount; this.future = Futures.futureWithTimeout(timeout, executor); }
/** * Triggers a number of metadata cleanups by repeatedly appending to a random new segment until a cleanup task is detected. * * @param expectedSegmentNames The segments that we are expecting to evict. */ CompletableFuture<Void> triggerMetadataCleanup(Collection<String> expectedSegmentNames) { String tempSegmentName = getSegmentName(Long.hashCode(System.nanoTime())); HashSet<String> remainingSegments = new HashSet<>(expectedSegmentNames); CompletableFuture<Void> cleanupTask = Futures.futureWithTimeout(TIMEOUT, this.executor); // Inject this callback into the MetadataCleaner callback, which was setup for us in createMetadataCleaner(). this.metadataCleanupFinishedCallback = evictedSegmentNames -> { remainingSegments.removeAll(evictedSegmentNames); if (remainingSegments.size() == 0) { cleanupTask.complete(null); } }; CompletableFuture<Void> af = appendRandomly(tempSegmentName, true, () -> !cleanupTask.isDone()); Futures.exceptionListener(af, cleanupTask::completeExceptionally); return cleanupTask; }
/** * Triggers at least one attribute cleanup for the given segment by continuously appending data (with no attributes) * for that segment until a cleanup is detected * * @param segmentName The segment we are trying to evict attributes for. */ CompletableFuture<Void> triggerAttributeCleanup(String segmentName) { CompletableFuture<Void> cleanupTask = Futures.futureWithTimeout(TIMEOUT, this.executor); SegmentMetadata sm = super.metadata.getStreamSegmentMetadata(super.metadata.getStreamSegmentId(segmentName, false)); // Inject this callback into the MetadataCleaner callback, which was setup for us in createMetadataCleaner(). this.metadataCleanupFinishedCallback = ignored -> { boolean onlyCoreAttributes = sm.getAttributes().keySet().stream().allMatch(Attributes::isCoreAttribute); if (onlyCoreAttributes) { cleanupTask.complete(null); } }; CompletableFuture<Void> af = appendRandomly(segmentName, false, () -> !cleanupTask.isDone()); Futures.exceptionListener(af, cleanupTask::completeExceptionally); return cleanupTask; }
private CompletableFuture<Void> waitForAdd(long currentSeqNo, Duration timeout) { CompletableFuture<Void> result; synchronized (this.lock) { Operation last = this.log.getLast(); if (last != null && last.getSequenceNumber() > currentSeqNo) { // An add has already been processed that meets or exceeds the given sequence number. result = CompletableFuture.completedFuture(null); } else { if (this.addProcessed == null) { // We need to wait for an add, and nobody else is waiting for it too. this.addProcessed = Futures.futureWithTimeout(timeout, this.executor); Futures.onTimeout(this.addProcessed, ex -> { synchronized (this.lock) { if (this.addProcessed.isCompletedExceptionally()) { this.addProcessed = null; } } }); } result = this.addProcessed; } } return result; }