Search in sources :

Example 26 with DataCorruptionException

use of io.pravega.segmentstore.server.DataCorruptionException in project pravega by pravega.

the class SegmentAggregatorTests method testSegmentMissingData.

/**
 * Tests the case when a Segment's data is missing from the ReadIndex (but the Segment itself is not deleted).
 */
@Test
public void testSegmentMissingData() throws Exception {
    final WriterConfig config = DEFAULT_CONFIG;
    @Cleanup TestContext context = new TestContext(config);
    context.segmentAggregator.initialize(TIMEOUT).join();
    // Add one operation big enough to trigger a Flush.
    byte[] appendData = new byte[config.getFlushThresholdBytes() + 1];
    StorageOperation appendOp = generateAppendAndUpdateMetadata(SEGMENT_ID, appendData, context);
    context.segmentAggregator.add(appendOp);
    Assert.assertTrue("Unexpected value returned by mustFlush() (size threshold).", context.segmentAggregator.mustFlush());
    // Clear the append data.
    context.dataSource.clearAppendData();
    // Call flush() and verify it throws DataCorruptionException.
    AssertExtensions.assertSuppliedFutureThrows("flush() did not throw when unable to read data from ReadIndex.", () -> context.segmentAggregator.flush(TIMEOUT), ex -> ex instanceof DataCorruptionException);
}
Also used : StorageOperation(io.pravega.segmentstore.server.logs.operations.StorageOperation) DataCorruptionException(io.pravega.segmentstore.server.DataCorruptionException) Cleanup(lombok.Cleanup) Test(org.junit.Test)

Example 27 with DataCorruptionException

use of io.pravega.segmentstore.server.DataCorruptionException in project pravega by pravega.

the class DurableLogTests method testRecoveryWithMetadataCleanup.

/**
 * Tests the following recovery scenario:
 * 1. A Segment is created and recorded in the metadata with some optional operations executing on it.
 * 2. The segment is evicted from the metadata.
 * 3. The segment is reactivated (with a new metadata mapping) - possibly due to an append. No truncation since #2.
 * 4. Recovery.
 */
@Test
public void testRecoveryWithMetadataCleanup() throws Exception {
    final long truncatedSeqNo = Integer.MAX_VALUE;
    // Setup a DurableLog and start it.
    @Cleanup TestDurableDataLogFactory dataLogFactory = new TestDurableDataLogFactory(new InMemoryDurableDataLogFactory(MAX_DATA_LOG_APPEND_SIZE, executorService()));
    @Cleanup Storage storage = InMemoryStorageFactory.newStorage(executorService());
    storage.initialize(1);
    long segmentId;
    // First DurableLog. We use this for generating data.
    val metadata1 = (StreamSegmentContainerMetadata) new MetadataBuilder(CONTAINER_ID).build();
    @Cleanup CacheStorage cacheStorage = new DirectMemoryCache(Integer.MAX_VALUE);
    @Cleanup CacheManager cacheManager = new CacheManager(CachePolicy.INFINITE, cacheStorage, executorService());
    SegmentProperties originalSegmentInfo;
    try (ReadIndex readIndex = new ContainerReadIndex(DEFAULT_READ_INDEX_CONFIG, metadata1, storage, cacheManager, executorService());
        DurableLog durableLog = new DurableLog(ContainerSetup.defaultDurableLogConfig(), metadata1, dataLogFactory, readIndex, executorService())) {
        durableLog.startAsync().awaitRunning();
        // Create the segment.
        val segmentIds = createStreamSegmentsWithOperations(1, durableLog);
        segmentId = segmentIds.stream().findFirst().orElse(-1L);
        // Evict the segment.
        val sm1 = metadata1.getStreamSegmentMetadata(segmentId);
        originalSegmentInfo = sm1.getSnapshot();
        // Simulate a truncation. This is needed in order to trigger a cleanup.
        metadata1.removeTruncationMarkers(truncatedSeqNo);
        val cleanedUpSegments = metadata1.cleanup(Collections.singleton(sm1), truncatedSeqNo);
        Assert.assertEquals("Unexpected number of segments evicted.", 1, cleanedUpSegments.size());
        // Map the segment again.
        val reMapOp = new StreamSegmentMapOperation(originalSegmentInfo);
        reMapOp.setStreamSegmentId(segmentId);
        durableLog.add(reMapOp, OperationPriority.Normal, TIMEOUT).join();
        // Stop.
        durableLog.stopAsync().awaitTerminated();
    }
    // Recovery #1. This should work well.
    val metadata2 = (StreamSegmentContainerMetadata) new MetadataBuilder(CONTAINER_ID).build();
    try (ReadIndex readIndex = new ContainerReadIndex(DEFAULT_READ_INDEX_CONFIG, metadata2, storage, cacheManager, executorService());
        DurableLog durableLog = new DurableLog(ContainerSetup.defaultDurableLogConfig(), metadata2, dataLogFactory, readIndex, executorService())) {
        durableLog.startAsync().awaitRunning();
        // Get segment info
        val recoveredSegmentInfo = metadata1.getStreamSegmentMetadata(segmentId).getSnapshot();
        Assert.assertEquals("Unexpected length from recovered segment.", originalSegmentInfo.getLength(), recoveredSegmentInfo.getLength());
        // Now evict the segment again ...
        val sm = metadata2.getStreamSegmentMetadata(segmentId);
        // Simulate a truncation. This is needed in order to trigger a cleanup.
        metadata2.removeTruncationMarkers(truncatedSeqNo);
        val cleanedUpSegments = metadata2.cleanup(Collections.singleton(sm), truncatedSeqNo);
        Assert.assertEquals("Unexpected number of segments evicted.", 1, cleanedUpSegments.size());
        // ... and re-map it with a new Id. This is a perfectly valid operation, and we can't prevent it.
        durableLog.add(new StreamSegmentMapOperation(originalSegmentInfo), OperationPriority.Normal, TIMEOUT).join();
        // Stop.
        durableLog.stopAsync().awaitTerminated();
    }
    // Recovery #2. This should fail due to the same segment mapped multiple times with different ids.
    val metadata3 = (StreamSegmentContainerMetadata) new MetadataBuilder(CONTAINER_ID).build();
    try (ReadIndex readIndex = new ContainerReadIndex(DEFAULT_READ_INDEX_CONFIG, metadata3, storage, cacheManager, executorService());
        DurableLog durableLog = new DurableLog(ContainerSetup.defaultDurableLogConfig(), metadata3, dataLogFactory, readIndex, executorService())) {
        AssertExtensions.assertThrows("Recovery did not fail with the expected exception in case of multi-mapping", () -> durableLog.startAsync().awaitRunning(), ex -> ex instanceof IllegalStateException && ex.getCause() instanceof DataCorruptionException && ex.getCause().getCause() instanceof MetadataUpdateException);
    }
}
Also used : lombok.val(lombok.val) CacheStorage(io.pravega.segmentstore.storage.cache.CacheStorage) DirectMemoryCache(io.pravega.segmentstore.storage.cache.DirectMemoryCache) MetadataBuilder(io.pravega.segmentstore.server.MetadataBuilder) ContainerReadIndex(io.pravega.segmentstore.server.reading.ContainerReadIndex) ReadIndex(io.pravega.segmentstore.server.ReadIndex) StreamSegmentMapOperation(io.pravega.segmentstore.server.logs.operations.StreamSegmentMapOperation) InMemoryDurableDataLogFactory(io.pravega.segmentstore.storage.mocks.InMemoryDurableDataLogFactory) Cleanup(lombok.Cleanup) ContainerReadIndex(io.pravega.segmentstore.server.reading.ContainerReadIndex) StreamSegmentContainerMetadata(io.pravega.segmentstore.server.containers.StreamSegmentContainerMetadata) Storage(io.pravega.segmentstore.storage.Storage) CacheStorage(io.pravega.segmentstore.storage.cache.CacheStorage) CacheManager(io.pravega.segmentstore.server.CacheManager) TestDurableDataLogFactory(io.pravega.segmentstore.server.TestDurableDataLogFactory) SegmentProperties(io.pravega.segmentstore.contracts.SegmentProperties) DataCorruptionException(io.pravega.segmentstore.server.DataCorruptionException) Test(org.junit.Test)

Example 28 with DataCorruptionException

use of io.pravega.segmentstore.server.DataCorruptionException in project pravega by pravega.

the class MemoryStateUpdaterTests method testProcessWithErrors.

/**
 * Tests the functionality of the {@link MemoryStateUpdater#process} method with critical errors.
 */
@Test
public void testProcessWithErrors() {
    final int corruptAtIndex = 10;
    final int segmentCount = 10;
    final int operationCountPerType = 5;
    // Add to MTL + Add to ReadIndex (append; beginMerge).
    val opLog = new OperationLogTestBase.CorruptedMemoryOperationLog(corruptAtIndex);
    val readIndex = mock(ReadIndex.class);
    MemoryStateUpdater updater = new MemoryStateUpdater(opLog, readIndex);
    AssertExtensions.assertThrows("Expected a DataCorruptionException", () -> populate(updater, segmentCount, operationCountPerType), ex -> ex instanceof DataCorruptionException);
    // Verify they were properly processed.
    verify(readIndex, never()).triggerFutureReads(anyCollection());
    Queue<Operation> logIterator = opLog.poll(corruptAtIndex * 2);
    int addCount = 0;
    while (!logIterator.isEmpty()) {
        addCount++;
        logIterator.poll();
    }
    Assert.assertEquals("Unexpected number of operations added to the log.", corruptAtIndex - 1, addCount);
// The rest of the checks is done in the populate() method: it verifies that the callback is invoked for every
// operation, including the failed ones.
}
Also used : lombok.val(lombok.val) DataCorruptionException(io.pravega.segmentstore.server.DataCorruptionException) MergeSegmentOperation(io.pravega.segmentstore.server.logs.operations.MergeSegmentOperation) Operation(io.pravega.segmentstore.server.logs.operations.Operation) StreamSegmentMapOperation(io.pravega.segmentstore.server.logs.operations.StreamSegmentMapOperation) SegmentOperation(io.pravega.segmentstore.server.SegmentOperation) CachedStreamSegmentAppendOperation(io.pravega.segmentstore.server.logs.operations.CachedStreamSegmentAppendOperation) StorageOperation(io.pravega.segmentstore.server.logs.operations.StorageOperation) StreamSegmentAppendOperation(io.pravega.segmentstore.server.logs.operations.StreamSegmentAppendOperation) Test(org.junit.Test)

Example 29 with DataCorruptionException

use of io.pravega.segmentstore.server.DataCorruptionException in project pravega by pravega.

the class SegmentAggregator method getFlushData.

/**
 * Returns a {@link BufferView} which contains the data needing to be flushed to Storage.
 *
 * @return A {@link BufferView} to flush or null if the segment was deleted.
 * @throws DataCorruptionException If a unable to retrieve required data from the Data Source.
 */
@Nullable
private BufferView getFlushData() throws DataCorruptionException {
    StorageOperation first = this.operations.getFirst();
    if (!(first instanceof AggregatedAppendOperation)) {
        // Nothing to flush - first operation is not an AggregatedAppend.
        return null;
    }
    AggregatedAppendOperation appendOp = (AggregatedAppendOperation) first;
    int length = (int) appendOp.getLength();
    BufferView data = null;
    if (length > 0) {
        data = this.dataSource.getAppendData(appendOp.getStreamSegmentId(), appendOp.getStreamSegmentOffset(), length);
        if (data == null) {
            if (this.metadata.isDeleted()) {
                // Segment was deleted - nothing more to do.
                return null;
            }
            throw new DataCorruptionException(String.format("Unable to retrieve CacheContents for '%s'.", appendOp));
        }
    }
    appendOp.seal();
    return data;
}
Also used : BufferView(io.pravega.common.util.BufferView) StorageOperation(io.pravega.segmentstore.server.logs.operations.StorageOperation) DataCorruptionException(io.pravega.segmentstore.server.DataCorruptionException) Nullable(javax.annotation.Nullable)

Example 30 with DataCorruptionException

use of io.pravega.segmentstore.server.DataCorruptionException in project pravega by pravega.

the class SegmentAggregator method initialize.

// endregion
// region Operations
/**
 * Initializes the SegmentAggregator by pulling information from the given Storage.
 *
 * @param timeout  Timeout for the operation.
 * @return A CompletableFuture that, when completed, will indicate that the operation finished successfully. If any
 * errors occurred during the operation, the Future will be completed with the appropriate exception.
 */
CompletableFuture<Void> initialize(Duration timeout) {
    Exceptions.checkNotClosed(isClosed(), this);
    Preconditions.checkState(this.state.get() == AggregatorState.NotInitialized, "SegmentAggregator has already been initialized.");
    assert this.handle.get() == null : "non-null handle but state == " + this.state.get();
    long traceId = LoggerHelpers.traceEnterWithContext(log, this.traceObjectId, "initialize");
    if (this.metadata.isDeleted()) {
        // Segment is dead on arrival. Delete it from Storage (if it exists) and do not bother to do anything else with it).
        // This is a rather uncommon case, but it can happen in one of two cases: 1) the segment has been deleted
        // immediately after creation or 2) after a container recovery.
        log.info("{}: Segment '{}' is marked as Deleted in Metadata. Attempting Storage delete.", this.traceObjectId, this.metadata.getName());
        return Futures.exceptionallyExpecting(this.storage.openWrite(this.metadata.getName()).thenComposeAsync(handle -> this.storage.delete(handle, timeout), this.executor), ex -> ex instanceof StreamSegmentNotExistsException, // It's OK if already deleted.
        null).thenRun(() -> {
            updateMetadataPostDeletion(this.metadata);
            log.info("{}: Segment '{}' is marked as Deleted in Metadata and has been deleted from Storage. Ignoring all further operations on it.", this.traceObjectId, this.metadata.getName());
            setState(AggregatorState.Writing);
            LoggerHelpers.traceLeave(log, this.traceObjectId, "initialize", traceId);
        });
    }
    // Segment not deleted.
    return openWrite(this.metadata.getName(), this.handle, timeout).thenAcceptAsync(segmentInfo -> {
        // Check & Update StorageLength in metadata.
        if (this.metadata.getStorageLength() != segmentInfo.getLength()) {
            if (this.metadata.getStorageLength() >= 0) {
                // Only log warning if the StorageLength has actually been initialized, but is different.
                log.info("{}: SegmentMetadata has a StorageLength ({}) that is different than the actual one ({}) - updating metadata.", this.traceObjectId, this.metadata.getStorageLength(), segmentInfo.getLength());
            }
            // It is very important to keep this value up-to-date and correct.
            this.metadata.setStorageLength(segmentInfo.getLength());
        }
        // Check if the Storage segment is sealed, but it's not in metadata (this is 100% indicative of some data corruption happening).
        if (segmentInfo.isSealed()) {
            if (!this.metadata.isSealed()) {
                throw new CompletionException(new DataCorruptionException(String.format("Segment '%s' is sealed in Storage but not in the metadata.", this.metadata.getName())));
            }
            if (!this.metadata.isSealedInStorage()) {
                this.metadata.markSealedInStorage();
                log.info("{}: Segment is sealed in Storage but metadata does not reflect that - updating metadata.", this.traceObjectId);
            }
        }
        log.info("{}: Initialized. StorageLength = {}, Sealed = {}.", this.traceObjectId, segmentInfo.getLength(), segmentInfo.isSealed());
        LoggerHelpers.traceLeave(log, this.traceObjectId, "initialize", traceId);
        setState(AggregatorState.Writing);
    }, this.executor).exceptionally(ex -> {
        ex = Exceptions.unwrap(ex);
        if (ex instanceof StreamSegmentNotExistsException) {
            // Segment does not exist in Storage. There are two possibilities here:
            if (this.metadata.getStorageLength() == 0 && !this.metadata.isDeletedInStorage()) {
                // Segment has never been created because there was nothing to write to it. As long as we know
                // its expected length is zero, this is a valid case.
                this.handle.set(null);
                log.info("{}: Initialized. Segment does not exist in Storage but Metadata indicates it should be empty.", this.traceObjectId);
                if (this.metadata.isSealed() && this.metadata.getLength() == 0) {
                    // Truly an empty segment that is sealed; mark it as such in Storage.
                    this.metadata.markSealedInStorage();
                    log.info("{}: Segment does not exist in Storage, but Metadata indicates it is empty and sealed - marking as sealed in storage.", this.traceObjectId);
                }
            } else {
                // Segment does not exist anymore. This is a real possibility during recovery, in the following cases:
                // * We already processed a Segment Deletion but did not have a chance to checkpoint metadata
                // * We processed a MergeSegmentOperation but did not have a chance to ack/truncate the DataSource
                // Update metadata, just in case it is not already updated.
                updateMetadataPostDeletion(this.metadata);
                log.info("{}: Segment '{}' does not exist in Storage. Ignoring all further operations on it.", this.traceObjectId, this.metadata.getName());
            }
            setState(AggregatorState.Writing);
            LoggerHelpers.traceLeave(log, this.traceObjectId, "initialize", traceId);
        } else {
            // Other kind of error - re-throw.
            throw new CompletionException(ex);
        }
        return null;
    });
}
Also used : Storage(io.pravega.segmentstore.storage.Storage) StreamSegmentInformation(io.pravega.segmentstore.contracts.StreamSegmentInformation) StreamSegmentNotExistsException(io.pravega.segmentstore.contracts.StreamSegmentNotExistsException) SneakyThrows(lombok.SneakyThrows) MergeSegmentOperation(io.pravega.segmentstore.server.logs.operations.MergeSegmentOperation) Cleanup(lombok.Cleanup) ServiceHaltException(io.pravega.segmentstore.server.ServiceHaltException) UpdateableSegmentMetadata(io.pravega.segmentstore.server.UpdateableSegmentMetadata) SegmentProperties(io.pravega.segmentstore.contracts.SegmentProperties) StreamSegmentSealedException(io.pravega.segmentstore.contracts.StreamSegmentSealedException) SegmentHandle(io.pravega.segmentstore.storage.SegmentHandle) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) BufferView(io.pravega.common.util.BufferView) Duration(java.time.Duration) Operation(io.pravega.segmentstore.server.logs.operations.Operation) WriterFlushResult(io.pravega.segmentstore.server.WriterFlushResult) StreamSegmentTruncateOperation(io.pravega.segmentstore.server.logs.operations.StreamSegmentTruncateOperation) Attributes(io.pravega.segmentstore.contracts.Attributes) Predicate(java.util.function.Predicate) CompletionException(java.util.concurrent.CompletionException) ThreadSafe(javax.annotation.concurrent.ThreadSafe) GuardedBy(javax.annotation.concurrent.GuardedBy) Collectors(java.util.stream.Collectors) List(java.util.List) Slf4j(lombok.extern.slf4j.Slf4j) StreamSegmentExistsException(io.pravega.segmentstore.contracts.StreamSegmentExistsException) BadOffsetException(io.pravega.segmentstore.contracts.BadOffsetException) WriterSegmentProcessor(io.pravega.segmentstore.server.WriterSegmentProcessor) Futures(io.pravega.common.concurrent.Futures) Getter(lombok.Getter) SegmentRollingPolicy(io.pravega.segmentstore.storage.SegmentRollingPolicy) Exceptions(io.pravega.common.Exceptions) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) CompletableFuture(java.util.concurrent.CompletableFuture) AtomicReference(java.util.concurrent.atomic.AtomicReference) Supplier(java.util.function.Supplier) AbstractTimer(io.pravega.common.AbstractTimer) SegmentMetadata(io.pravega.segmentstore.server.SegmentMetadata) Nullable(javax.annotation.Nullable) LoggerHelpers(io.pravega.common.LoggerHelpers) TimeoutTimer(io.pravega.common.TimeoutTimer) Executor(java.util.concurrent.Executor) AtomicLong(java.util.concurrent.atomic.AtomicLong) SegmentOperation(io.pravega.segmentstore.server.SegmentOperation) CachedStreamSegmentAppendOperation(io.pravega.segmentstore.server.logs.operations.CachedStreamSegmentAppendOperation) StorageOperation(io.pravega.segmentstore.server.logs.operations.StorageOperation) StreamSegmentAppendOperation(io.pravega.segmentstore.server.logs.operations.StreamSegmentAppendOperation) Preconditions(com.google.common.base.Preconditions) DataCorruptionException(io.pravega.segmentstore.server.DataCorruptionException) ArrayDeque(java.util.ArrayDeque) DeleteSegmentOperation(io.pravega.segmentstore.server.logs.operations.DeleteSegmentOperation) StreamSegmentSealOperation(io.pravega.segmentstore.server.logs.operations.StreamSegmentSealOperation) InputStream(java.io.InputStream) CompletionException(java.util.concurrent.CompletionException) DataCorruptionException(io.pravega.segmentstore.server.DataCorruptionException) StreamSegmentNotExistsException(io.pravega.segmentstore.contracts.StreamSegmentNotExistsException)

Aggregations

DataCorruptionException (io.pravega.segmentstore.server.DataCorruptionException)34 Test (org.junit.Test)17 Cleanup (lombok.Cleanup)16 lombok.val (lombok.val)16 CachedStreamSegmentAppendOperation (io.pravega.segmentstore.server.logs.operations.CachedStreamSegmentAppendOperation)12 StorageOperation (io.pravega.segmentstore.server.logs.operations.StorageOperation)12 Operation (io.pravega.segmentstore.server.logs.operations.Operation)10 StreamSegmentAppendOperation (io.pravega.segmentstore.server.logs.operations.StreamSegmentAppendOperation)10 CompletionException (java.util.concurrent.CompletionException)10 UpdateableSegmentMetadata (io.pravega.segmentstore.server.UpdateableSegmentMetadata)9 SegmentMetadata (io.pravega.segmentstore.server.SegmentMetadata)8 MergeSegmentOperation (io.pravega.segmentstore.server.logs.operations.MergeSegmentOperation)8 StreamSegmentSealOperation (io.pravega.segmentstore.server.logs.operations.StreamSegmentSealOperation)8 Storage (io.pravega.segmentstore.storage.Storage)8 StreamSegmentNotExistsException (io.pravega.segmentstore.contracts.StreamSegmentNotExistsException)7 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)7 Exceptions (io.pravega.common.Exceptions)6 StreamSegmentSealedException (io.pravega.segmentstore.contracts.StreamSegmentSealedException)6 Duration (java.time.Duration)6 TimeoutTimer (io.pravega.common.TimeoutTimer)4