private CompletableFuture<StreamCutRecord> generateStreamCutIfRequired(String scope, String stream, StreamCutReferenceRecord previous, long recordingTime, OperationContext context, String delegationToken) { if (previous == null || recordingTime - previous.getRecordingTime() > RETENTION_FREQUENCY_IN_MINUTES) { return Futures.exceptionallyComposeExpecting( previous == null ? CompletableFuture.completedFuture(null) : streamMetadataStore.getStreamCutRecord(scope, stream, previous, context, executor), e -> e instanceof StoreException.DataNotFoundException, () -> null) .thenCompose(previousRecord -> generateStreamCut(scope, stream, previousRecord, context, delegationToken) .thenCompose(newRecord -> streamMetadataStore.addStreamCutToRetentionSet(scope, stream, newRecord, context, executor) .thenApply(x -> { log.debug("New streamCut generated for stream {}/{}", scope, stream); return newRecord; }))); } else { return CompletableFuture.completedFuture(null); } }
/** * Performs a flush attempt, and retries it in case it failed with {@link BadAttributeUpdateException} for the * {@link Attributes#TABLE_INDEX_OFFSET} attribute. * * In case of a retry, it reconciles the cached value for the {@link Attributes#TABLE_INDEX_OFFSET} attribute and * updates any internal state. * * @param segment A {@link DirectSegmentAccess} representing the Segment to flush on. * @param timer Timer for the operation. * @return A CompletableFuture that, when completed, will indicate the flush has completed successfully. If the * operation failed, it will be failed with the appropriate exception. Notable exceptions: * <ul> * <li>{@link BadAttributeUpdateException} If a conditional update on the {@link Attributes#TABLE_INDEX_OFFSET} attribute * failed (for the second attempt) or on any other attribute failed (for any attempt). * <li>{@link DataCorruptionException} If the reconciliation failed (in which case no flush is attempted. * </ul> */ private CompletableFuture<Long> flushWithSingleRetry(DirectSegmentAccess segment, TimeoutTimer timer) { return Futures.exceptionallyComposeExpecting( flushOnce(segment, timer), this::canRetryFlushException, () -> { reconcileTableIndexOffset(); return flushOnce(segment, timer); }); }
/** * Initializes the SegmentAttributeIndex by inspecting the AttributeSegmentFile and creating it if needed. * * @param timeout Timeout for the operation. * @return A CompletableFuture that, when completed, will indicate the operation has succeeded. */ CompletableFuture<Void> initialize(Duration timeout) { TimeoutTimer timer = new TimeoutTimer(timeout); Preconditions.checkState(!this.index.isInitialized(), "SegmentAttributeIndex is already initialized."); String attributeSegmentName = StreamSegmentNameUtils.getAttributeSegmentName(this.segmentMetadata.getName()); // Attempt to open the Attribute Segment; if it does not exist yet then create it. return Futures .exceptionallyComposeExpecting( this.storage.openWrite(attributeSegmentName).thenAccept(this.handle::set), ex -> ex instanceof StreamSegmentNotExistsException, () -> this.storage.create(attributeSegmentName, this.config.getAttributeSegmentRollingPolicy(), timer.getRemaining()).thenAccept(this.handle::set)) .thenComposeAsync(v -> this.index.initialize(timer.getRemaining()), this.executor) .thenRun(() -> log.debug("{}: Initialized.", this.traceObjectId)) .exceptionally(this::handleIndexOperationException); }