use of io.pravega.segmentstore.server.logs.operations.Operation in project pravega by pravega.
the class MemoryStateUpdater method process.
/**
* Processes the given operations and applies them to the ReadIndex and InMemory OperationLog.
*
* @param operations An Iterator iterating over the operations to process (in sequence).
* @throws DataCorruptionException If a serious, non-recoverable, data corruption was detected, such as trying to
* append operations out of order.
*/
void process(Iterator<Operation> operations) throws DataCorruptionException {
HashSet<Long> segmentIds = new HashSet<>();
while (operations.hasNext()) {
Operation op = operations.next();
process(op);
if (op instanceof SegmentOperation) {
// Record recent activity on stream segment, if applicable. This should be recorded for any kind
// of Operation that touches a Segment, since when we issue 'triggerFutureReads' on the readIndex,
// it should include 'sealed' StreamSegments too - any Future Reads waiting on that Offset will be cancelled.
segmentIds.add(((SegmentOperation) op).getStreamSegmentId());
}
}
if (!this.recoveryMode.get()) {
// Trigger Future Reads on those segments which were touched by Appends or Seals.
this.readIndex.triggerFutureReads(segmentIds);
if (this.commitSuccess != null) {
this.commitSuccess.run();
}
}
}
use of io.pravega.segmentstore.server.logs.operations.Operation in project pravega by pravega.
the class RecoveryProcessor method recoverOperation.
protected void recoverOperation(DataFrameRecord<Operation> dataFrameRecord, OperationMetadataUpdater metadataUpdater) throws DataCorruptionException {
// Update Metadata Sequence Number.
Operation operation = dataFrameRecord.getItem();
metadataUpdater.setOperationSequenceNumber(operation.getSequenceNumber());
// Update the metadata with the information from the Operation.
try {
log.debug("{} Recovering {}.", this.traceObjectId, operation);
metadataUpdater.preProcessOperation(operation);
metadataUpdater.acceptOperation(operation);
} catch (StreamSegmentException | ContainerException ex) {
// Metadata update failures should not happen during recovery.
throw new DataCorruptionException(String.format("Unable to update metadata for Log Operation '%s'.", operation), ex);
}
// Update in-memory structures.
this.stateUpdater.process(operation);
}
use of io.pravega.segmentstore.server.logs.operations.Operation in project pravega by pravega.
the class SegmentAggregator method reconcileAppendOperation.
/**
* Attempts to reconcile the given Append Operation. Since Append Operations can be partially flushed, reconciliation
* may be for the full operation or for a part of it.
*
* @param op The Operation (StreamSegmentAppendOperation or CachedStreamSegmentAppendOperation) to reconcile.
* @param storageInfo The current state of the Segment in Storage.
* @param timer Timer for the operation.
* @return A CompletableFuture containing a FlushResult with the number of bytes reconciled, or failed with a ReconciliationFailureException,
* if the operation cannot be reconciled, based on the in-memory metadata or the current state of the Segment in Storage.
*/
private CompletableFuture<FlushResult> reconcileAppendOperation(StorageOperation op, SegmentProperties storageInfo, TimeoutTimer timer) {
Preconditions.checkArgument(op instanceof AggregatedAppendOperation, "Not given an append operation.");
// Read data from Storage, and compare byte-by-byte.
InputStream appendStream = this.dataSource.getAppendData(op.getStreamSegmentId(), op.getStreamSegmentOffset(), (int) op.getLength());
if (appendStream == null) {
return Futures.failedFuture(new ReconciliationFailureException(String.format("Unable to reconcile operation '%s' because no append data is associated with it.", op), this.metadata, storageInfo));
}
// Only read as much data as we need.
long readLength = Math.min(op.getLastStreamSegmentOffset(), storageInfo.getLength()) - op.getStreamSegmentOffset();
assert readLength > 0 : "Append Operation to be reconciled is beyond the Segment's StorageLength " + op;
AtomicInteger bytesReadSoFar = new AtomicInteger();
// Read all data from storage.
byte[] storageData = new byte[(int) readLength];
return Futures.loop(() -> bytesReadSoFar.get() < readLength, () -> this.storage.read(this.handle.get(), op.getStreamSegmentOffset() + bytesReadSoFar.get(), storageData, bytesReadSoFar.get(), (int) readLength - bytesReadSoFar.get(), timer.getRemaining()), bytesRead -> {
assert bytesRead > 0 : String.format("Unable to make any read progress when reconciling operation '%s' after reading %s bytes.", op, bytesReadSoFar);
bytesReadSoFar.addAndGet(bytesRead);
}, this.executor).thenApplyAsync(v -> {
// Compare, byte-by-byte, the contents of the append.
verifySame(appendStream, storageData, op, storageInfo);
if (readLength >= op.getLength() && op.getLastStreamSegmentOffset() <= storageInfo.getLength()) {
// Operation has been completely validated; pop it off the list.
StorageOperation removedOp = this.operations.removeFirst();
assert op == removedOp : "Reconciled operation is not the same as removed operation";
}
return new FlushResult().withFlushedBytes(readLength);
}, this.executor);
}
use of io.pravega.segmentstore.server.logs.operations.Operation in project pravega by pravega.
the class StreamSegmentContainer method sealStreamSegment.
@Override
public CompletableFuture<Long> sealStreamSegment(String streamSegmentName, Duration timeout) {
ensureRunning();
logRequest("sealStreamSegment", streamSegmentName);
this.metrics.seal();
TimeoutTimer timer = new TimeoutTimer(timeout);
AtomicReference<StreamSegmentSealOperation> operation = new AtomicReference<>();
return this.segmentMapper.getOrAssignStreamSegmentId(streamSegmentName, timer.getRemaining(), streamSegmentId -> {
operation.set(new StreamSegmentSealOperation(streamSegmentId));
return this.durableLog.add(operation.get(), timer.getRemaining());
}).thenApply(seqNo -> operation.get().getStreamSegmentOffset());
}
use of io.pravega.segmentstore.server.logs.operations.Operation in project pravega by pravega.
the class DurableLogTests method testRecoveryWithDisabledDataLog.
/**
* Verifies the ability of hte DurableLog to recover (delayed start) using a disabled DurableDataLog. This verifies
* the ability to shut down correctly while still waiting for the DataLog to become enabled as well as detecting that
* it did become enabled and then resume normal operations.
*/
@Test
public void testRecoveryWithDisabledDataLog() throws Exception {
int streamSegmentCount = 50;
int appendsPerStreamSegment = 20;
AtomicReference<TestDurableDataLog> dataLog = new AtomicReference<>();
@Cleanup TestDurableDataLogFactory dataLogFactory = new TestDurableDataLogFactory(new InMemoryDurableDataLogFactory(MAX_DATA_LOG_APPEND_SIZE, executorService()), dataLog::set);
@Cleanup Storage storage = InMemoryStorageFactory.newStorage(executorService());
storage.initialize(1);
@Cleanup InMemoryCacheFactory cacheFactory = new InMemoryCacheFactory();
@Cleanup CacheManager cacheManager = new CacheManager(DEFAULT_READ_INDEX_CONFIG.getCachePolicy(), executorService());
// Write some data to the log. We'll read it later.
HashSet<Long> streamSegmentIds;
List<Operation> originalOperations;
List<OperationWithCompletion> completionFutures;
UpdateableContainerMetadata metadata = new MetadataBuilder(CONTAINER_ID).build();
dataLog.set(null);
try (ReadIndex readIndex = new ContainerReadIndex(DEFAULT_READ_INDEX_CONFIG, metadata, cacheFactory, storage, cacheManager, executorService());
DurableLog durableLog = new DurableLog(ContainerSetup.defaultDurableLogConfig(), metadata, dataLogFactory, readIndex, executorService())) {
// DurableLog should start properly.
durableLog.startAsync().awaitRunning();
streamSegmentIds = createStreamSegmentsWithOperations(streamSegmentCount, metadata, durableLog, storage);
List<Operation> operations = generateOperations(streamSegmentIds, new HashMap<>(), appendsPerStreamSegment, METADATA_CHECKPOINT_EVERY, false, false);
completionFutures = processOperations(operations, durableLog);
OperationWithCompletion.allOf(completionFutures).join();
originalOperations = readUpToSequenceNumber(durableLog, metadata.getOperationSequenceNumber());
}
// Disable the DurableDataLog. This requires us to initialize the log, then disable it.
metadata = new MetadataBuilder(CONTAINER_ID).build();
dataLog.set(null);
try (ReadIndex readIndex = new ContainerReadIndex(DEFAULT_READ_INDEX_CONFIG, metadata, cacheFactory, storage, cacheManager, executorService());
DurableLog durableLog = new DurableLog(ContainerSetup.defaultDurableLogConfig(), metadata, dataLogFactory, readIndex, executorService())) {
// DurableLog should start properly.
durableLog.startAsync().awaitRunning();
CompletableFuture<Void> online = durableLog.awaitOnline();
Assert.assertTrue("awaitOnline() returned an incomplete future.", Futures.isSuccessful(online));
Assert.assertFalse("Not expecting an offline DurableLog.", durableLog.isOffline());
dataLog.get().disable();
}
// Verify that the DurableLog starts properly and that all operations throw appropriate exceptions.
try (ReadIndex readIndex = new ContainerReadIndex(DEFAULT_READ_INDEX_CONFIG, metadata, cacheFactory, storage, cacheManager, executorService());
DurableLog durableLog = new DurableLog(ContainerSetup.defaultDurableLogConfig(), metadata, dataLogFactory, readIndex, executorService())) {
// DurableLog should start properly.
durableLog.startAsync().awaitRunning();
CompletableFuture<Void> online = durableLog.awaitOnline();
Assert.assertFalse("awaitOnline() returned a completed future.", online.isDone());
Assert.assertTrue("Expecting an offline DurableLog.", durableLog.isOffline());
// Verify all operations fail with the right exception.
AssertExtensions.assertThrows("add() did not fail with the right exception when offline.", () -> durableLog.add(new ProbeOperation(), TIMEOUT), ex -> ex instanceof ContainerOfflineException);
AssertExtensions.assertThrows("read() did not fail with the right exception when offline.", () -> durableLog.read(0, 1, TIMEOUT), ex -> ex instanceof ContainerOfflineException);
AssertExtensions.assertThrows("truncate() did not fail with the right exception when offline.", () -> durableLog.truncate(0, TIMEOUT), ex -> ex instanceof ContainerOfflineException);
AssertExtensions.assertThrows("operationProcessingBarrier() did not fail with the right exception when offline.", () -> durableLog.operationProcessingBarrier(TIMEOUT), ex -> ex instanceof ContainerOfflineException);
// Verify we can also shut it down properly from this state.
durableLog.stopAsync().awaitTerminated();
Assert.assertTrue("awaitOnline() returned future did not fail when DurableLog shut down.", online.isCompletedExceptionally());
}
// Verify that, when the DurableDataLog becomes enabled, the DurableLog can pick up the change and resume normal operations.
// Verify that the DurableLog starts properly and that all operations throw appropriate exceptions.
dataLog.set(null);
try (ReadIndex readIndex = new ContainerReadIndex(DEFAULT_READ_INDEX_CONFIG, metadata, cacheFactory, storage, cacheManager, executorService());
DurableLog durableLog = new DurableLog(ContainerSetup.defaultDurableLogConfig(), metadata, dataLogFactory, readIndex, executorService())) {
// DurableLog should start properly.
durableLog.startAsync().awaitRunning();
CompletableFuture<Void> online = durableLog.awaitOnline();
Assert.assertFalse("awaitOnline() returned a completed future.", online.isDone());
// Enable the underlying data log and await for recovery to finish.
dataLog.get().enable();
online.get(START_RETRY_DELAY_MILLIS * 100, TimeUnit.MILLISECONDS);
Assert.assertFalse("Not expecting an offline DurableLog after re-enabling.", durableLog.isOffline());
// Verify we can still read the data that we wrote before the DataLog was disabled.
List<Operation> recoveredOperations = readUpToSequenceNumber(durableLog, metadata.getOperationSequenceNumber());
assertRecoveredOperationsMatch(originalOperations, recoveredOperations);
performMetadataChecks(streamSegmentIds, new HashSet<>(), new HashMap<>(), completionFutures, metadata, false, false);
performReadIndexChecks(completionFutures, readIndex);
// Stop the processor.
durableLog.stopAsync().awaitTerminated();
}
}
Aggregations