Search in sources :

Example 11 with StreamSegmentMapOperation

use of io.pravega.segmentstore.server.logs.operations.StreamSegmentMapOperation in project pravega by pravega.

the class ContainerMetadataUpdateTransactionTests method testSegmentMapMax.

/**
 * Tests the ability to reject new StreamSegment/Transaction map operations that would exceed the max allowed counts.
 */
@Test
public void testSegmentMapMax() throws Exception {
    UpdateableContainerMetadata metadata = new MetadataBuilder(CONTAINER_ID).withMaxActiveSegmentCount(3).build();
    metadata.mapStreamSegmentId("a", SEGMENT_ID);
    metadata.mapStreamSegmentId("a_txn1", 123457, SEGMENT_ID);
    // Non-recovery mode.
    val txn1 = createUpdateTransaction(metadata);
    // Map one segment, which should fill up the quota.
    StreamSegmentMapOperation acceptedMap = createMap();
    txn1.preProcessOperation(acceptedMap);
    txn1.acceptOperation(acceptedMap);
    // Verify non-recovery mode.
    AssertExtensions.assertThrows("Unexpected behavior from preProcessOperation when attempting to map a StreamSegment that would exceed the active segment quota.", () -> txn1.preProcessOperation(createMap("foo")), ex -> ex instanceof TooManyActiveSegmentsException);
    AssertExtensions.assertThrows("Unexpected behavior from preProcessOperation when attempting to map a StreamSegment that would exceed the active segment quota.", () -> txn1.preProcessOperation(createTransactionMap(SEGMENT_ID, "foo")), ex -> ex instanceof TooManyActiveSegmentsException);
    // Verify recovery mode.
    metadata.enterRecoveryMode();
    val txn2 = createUpdateTransaction(metadata);
    // updater.setOperationSequenceNumber(10000);
    StreamSegmentMapOperation secondMap = createMap("c");
    secondMap.setStreamSegmentId(1234);
    txn2.preProcessOperation(secondMap);
    txn2.acceptOperation(secondMap);
    StreamSegmentMapOperation secondTxnMap = createTransactionMap(SEGMENT_ID, "a_txn2");
    secondTxnMap.setStreamSegmentId(1235);
    txn2.preProcessOperation(secondTxnMap);
    txn2.acceptOperation(secondTxnMap);
    txn2.commit(metadata);
    metadata.exitRecoveryMode();
    Assert.assertNotNull("Updater did not create metadata for new segment in recovery mode even if quota is exceeded.", metadata.getStreamSegmentMetadata(secondMap.getStreamSegmentId()));
    Assert.assertNotNull("Updater did not create metadata for new transaction in recovery mode even if quota is exceeded.", metadata.getStreamSegmentMetadata(secondTxnMap.getStreamSegmentId()));
}
Also used : lombok.val(lombok.val) MetadataBuilder(io.pravega.segmentstore.server.MetadataBuilder) StreamSegmentMapOperation(io.pravega.segmentstore.server.logs.operations.StreamSegmentMapOperation) UpdateableContainerMetadata(io.pravega.segmentstore.server.UpdateableContainerMetadata) TooManyActiveSegmentsException(io.pravega.segmentstore.contracts.TooManyActiveSegmentsException) Test(org.junit.Test)

Example 12 with StreamSegmentMapOperation

use of io.pravega.segmentstore.server.logs.operations.StreamSegmentMapOperation in project pravega by pravega.

the class ContainerMetadataUpdateTransactionTests method testPreProcessTransactionMap.

/**
 * Tests the processOperation and acceptOperation methods with TransactionMap operations.
 */
@Test
public void testPreProcessTransactionMap() throws Exception {
    UpdateableContainerMetadata metadata = createBlankMetadata();
    val txn1 = createUpdateTransaction(metadata);
    // Parent does not exist.
    AssertExtensions.assertThrows("Unexpected behavior from preProcessOperation when attempting to map a Transaction StreamSegment to an inexistent parent.", () -> txn1.preProcessOperation(createTransactionMap(12345)), ex -> ex instanceof MetadataUpdateException);
    // Brand new Transaction (and parent).
    StreamSegmentMapOperation mapParent = createMap();
    // Create parent.
    txn1.preProcessOperation(mapParent);
    txn1.acceptOperation(mapParent);
    txn1.commit(metadata);
    // Part 1: recovery mode.
    metadata.enterRecoveryMode();
    StreamSegmentMapOperation mapOp = createTransactionMap(mapParent.getStreamSegmentId());
    val txn2 = createUpdateTransaction(metadata);
    txn2.preProcessOperation(mapOp);
    Assert.assertEquals("preProcessOperation changed the StreamSegmentId on the operation in recovery mode.", ContainerMetadata.NO_STREAM_SEGMENT_ID, mapOp.getStreamSegmentId());
    // Part 2: non-recovery mode.
    metadata.exitRecoveryMode();
    val txn3 = createUpdateTransaction(metadata);
    txn3.preProcessOperation(mapOp);
    Assert.assertNotEquals("preProcessOperation did not set the StreamSegmentId on the operation.", ContainerMetadata.NO_STREAM_SEGMENT_ID, mapOp.getStreamSegmentId());
    Assert.assertNull("preProcessOperation modified the current transaction.", txn3.getStreamSegmentMetadata(mapOp.getStreamSegmentId()));
    Assert.assertNull("preProcessOperation modified the underlying metadata.", metadata.getStreamSegmentMetadata(mapOp.getStreamSegmentId()));
    txn3.acceptOperation(mapOp);
    val updaterMetadata = txn3.getStreamSegmentMetadata(mapOp.getStreamSegmentId());
    Assert.assertEquals("Unexpected StorageLength after call to processMetadataOperation (in transaction).", mapOp.getLength(), updaterMetadata.getStorageLength());
    Assert.assertEquals("Unexpected Length after call to processMetadataOperation (in transaction).", mapOp.getLength(), updaterMetadata.getLength());
    Assert.assertEquals("Unexpected value for isSealed after call to processMetadataOperation (in transaction).", mapOp.isSealed(), updaterMetadata.isSealed());
    Assert.assertEquals("Unexpected value for StartOffset after call to processMetadataOperation (in transaction).", 0, updaterMetadata.getStartOffset());
    Assert.assertNull("processMetadataOperation modified the underlying metadata.", metadata.getStreamSegmentMetadata(mapOp.getStreamSegmentId()));
    // Transaction StreamSegmentName exists (transaction).
    AssertExtensions.assertThrows("Unexpected behavior from preProcessOperation when a TransactionStreamSegment with the same Name already exists (in transaction).", () -> txn3.preProcessOperation(createTransactionMap(mapParent.getStreamSegmentId(), mapOp.getStreamSegmentName())), ex -> ex instanceof MetadataUpdateException);
    // Make changes permanent.
    txn3.commit(metadata);
    val segmentMetadata = metadata.getStreamSegmentMetadata(mapOp.getStreamSegmentId());
    AssertExtensions.assertMapEquals("Unexpected attributes in SegmentMetadata after call to commit().", mapOp.getAttributes(), segmentMetadata.getAttributes());
    // Transaction StreamSegmentName exists (metadata).
    AssertExtensions.assertThrows("Unexpected behavior from preProcessOperation when a TransactionStreamSegment with the same Name already exists (in metadata).", () -> txn3.preProcessOperation(createTransactionMap(mapParent.getStreamSegmentId(), mapOp.getStreamSegmentName())), ex -> ex instanceof MetadataUpdateException);
    // StreamSegmentName already exists and we try to map with the same id. Verify that we are able to update its
    // StorageLength (if different).
    val updateMap = new StreamSegmentMapOperation(mapOp.getParentStreamSegmentId(), StreamSegmentInformation.builder().name(mapOp.getStreamSegmentName()).startOffset(// Purposefully setting this wrong to see if it is auto-corrected.
    1).length(mapOp.getLength() + 1).sealed(true).attributes(createAttributes()).build());
    updateMap.setStreamSegmentId(mapOp.getStreamSegmentId());
    txn3.preProcessOperation(updateMap);
    txn3.acceptOperation(updateMap);
    Assert.assertEquals("Unexpected StorageLength after call to acceptOperation with remap (in transaction).", updateMap.getLength(), txn3.getStreamSegmentMetadata(mapOp.getStreamSegmentId()).getLength());
    txn3.commit(metadata);
    Assert.assertEquals("Unexpected StorageLength after call to acceptOperation with remap (post-commit).", updateMap.getLength(), metadata.getStreamSegmentMetadata(mapOp.getStreamSegmentId()).getLength());
    Assert.assertEquals("Unexpected StartOffset after call to acceptOperation with remap (post-commit).", 0, metadata.getStreamSegmentMetadata(mapOp.getStreamSegmentId()).getStartOffset());
}
Also used : lombok.val(lombok.val) StreamSegmentMapOperation(io.pravega.segmentstore.server.logs.operations.StreamSegmentMapOperation) UpdateableContainerMetadata(io.pravega.segmentstore.server.UpdateableContainerMetadata) Test(org.junit.Test)

Example 13 with StreamSegmentMapOperation

use of io.pravega.segmentstore.server.logs.operations.StreamSegmentMapOperation 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 InMemoryCacheFactory cacheFactory = new InMemoryCacheFactory();
    @Cleanup CacheManager cacheManager = new CacheManager(DEFAULT_READ_INDEX_CONFIG.getCachePolicy(), executorService());
    SegmentProperties originalSegmentInfo;
    try (ReadIndex readIndex = new ContainerReadIndex(DEFAULT_READ_INDEX_CONFIG, metadata1, cacheFactory, storage, cacheManager, executorService());
        DurableLog durableLog = new DurableLog(ContainerSetup.defaultDurableLogConfig(), metadata1, dataLogFactory, readIndex, executorService())) {
        durableLog.startAsync().awaitRunning();
        // Create the segment.
        val segmentIds = createStreamSegmentsWithOperations(1, metadata1, durableLog, storage);
        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, 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, cacheFactory, 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), 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, cacheFactory, 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) MetadataBuilder(io.pravega.segmentstore.server.MetadataBuilder) ContainerReadIndex(io.pravega.segmentstore.server.reading.ContainerReadIndex) ReadIndex(io.pravega.segmentstore.server.ReadIndex) InMemoryCacheFactory(io.pravega.segmentstore.storage.mocks.InMemoryCacheFactory) 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) CacheManager(io.pravega.segmentstore.server.reading.CacheManager) TestDurableDataLogFactory(io.pravega.segmentstore.server.TestDurableDataLogFactory) SegmentProperties(io.pravega.segmentstore.contracts.SegmentProperties) DataCorruptionException(io.pravega.segmentstore.server.DataCorruptionException) Test(org.junit.Test)

Example 14 with StreamSegmentMapOperation

use of io.pravega.segmentstore.server.logs.operations.StreamSegmentMapOperation in project pravega by pravega.

the class StorageWriterTests method createTransactions.

private HashMap<Long, ArrayList<Long>> createTransactions(Collection<Long> segmentIds, TestContext context) {
    // Create the Transactions.
    HashMap<Long, ArrayList<Long>> transactions = new HashMap<>();
    long transactionId = Integer.MAX_VALUE;
    for (long parentId : segmentIds) {
        ArrayList<Long> segmentTransactions = new ArrayList<>();
        transactions.put(parentId, segmentTransactions);
        SegmentMetadata parentMetadata = context.metadata.getStreamSegmentMetadata(parentId);
        for (int i = 0; i < TRANSACTIONS_PER_SEGMENT; i++) {
            String transactionName = StreamSegmentNameUtils.getTransactionNameFromId(parentMetadata.getName(), UUID.randomUUID());
            context.metadata.mapStreamSegmentId(transactionName, transactionId, parentId);
            initializeSegment(transactionId, context);
            segmentTransactions.add(transactionId);
            // Add the operation to the log.
            StreamSegmentMapOperation mapOp = new StreamSegmentMapOperation(parentId, context.storage.getStreamSegmentInfo(transactionName, TIMEOUT).join());
            mapOp.setStreamSegmentId(transactionId);
            context.dataSource.add(mapOp);
            transactionId++;
        }
    }
    return transactions;
}
Also used : UpdateableSegmentMetadata(io.pravega.segmentstore.server.UpdateableSegmentMetadata) SegmentMetadata(io.pravega.segmentstore.server.SegmentMetadata) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) StreamSegmentMapOperation(io.pravega.segmentstore.server.logs.operations.StreamSegmentMapOperation)

Example 15 with StreamSegmentMapOperation

use of io.pravega.segmentstore.server.logs.operations.StreamSegmentMapOperation in project pravega by pravega.

the class StreamSegmentMapper method submitToOperationLog.

/**
 * Submits a StreamSegmentMapOperation to the OperationLog. Upon completion, this operation
 * will have mapped the given Segment to a new internal Segment Id if none was provided in the given SegmentInfo.
 * If the given SegmentInfo already has a SegmentId set, then all efforts will be made to map that Segment with the
 * requested Segment Id.
 *
 * @param segmentInfo           The SegmentInfo for the StreamSegment to generate and persist.
 * @param parentStreamSegmentId If different from ContainerMetadata.NO_STREAM_SEGMENT_ID, the given streamSegmentInfo
 *                              will be mapped as a transaction. Otherwise, this will be registered as a standalone StreamSegment.
 * @param timeout               Timeout for the operation.
 * @return A CompletableFuture that, when completed, will contain the internal SegmentId that was assigned (or the
 * one supplied via SegmentInfo, if any). If the operation failed, then this Future will complete with that exception.
 */
private CompletableFuture<Long> submitToOperationLog(SegmentInfo segmentInfo, long parentStreamSegmentId, Duration timeout) {
    SegmentProperties properties = segmentInfo.getProperties();
    if (properties.isDeleted()) {
        // Stream does not exist. Fail the request with the appropriate exception.
        failAssignment(properties.getName(), new StreamSegmentNotExistsException("StreamSegment does not exist."));
        return Futures.failedFuture(new StreamSegmentNotExistsException(properties.getName()));
    }
    long existingSegmentId = this.containerMetadata.getStreamSegmentId(properties.getName(), true);
    if (isValidStreamSegmentId(existingSegmentId)) {
        // Looks like someone else beat us to it.
        completeAssignment(properties.getName(), existingSegmentId);
        return CompletableFuture.completedFuture(existingSegmentId);
    } else {
        StreamSegmentMapOperation op;
        if (isValidStreamSegmentId(parentStreamSegmentId)) {
            // Transaction.
            SegmentMetadata parentMetadata = this.containerMetadata.getStreamSegmentMetadata(parentStreamSegmentId);
            assert parentMetadata != null : "parentMetadata is null";
            op = new StreamSegmentMapOperation(parentStreamSegmentId, properties);
        } else {
            // Standalone StreamSegment.
            op = new StreamSegmentMapOperation(properties);
        }
        if (segmentInfo.getSegmentId() != ContainerMetadata.NO_STREAM_SEGMENT_ID) {
            op.setStreamSegmentId(segmentInfo.getSegmentId());
        }
        return this.durableLog.add(op, timeout).thenApply(seqNo -> completeAssignment(properties.getName(), op.getStreamSegmentId()));
    }
}
Also used : SegmentMetadata(io.pravega.segmentstore.server.SegmentMetadata) StreamSegmentMapOperation(io.pravega.segmentstore.server.logs.operations.StreamSegmentMapOperation) SegmentProperties(io.pravega.segmentstore.contracts.SegmentProperties) StreamSegmentNotExistsException(io.pravega.segmentstore.contracts.StreamSegmentNotExistsException)

Aggregations

StreamSegmentMapOperation (io.pravega.segmentstore.server.logs.operations.StreamSegmentMapOperation)18 lombok.val (lombok.val)10 Test (org.junit.Test)9 UpdateableContainerMetadata (io.pravega.segmentstore.server.UpdateableContainerMetadata)6 MetadataCheckpointOperation (io.pravega.segmentstore.server.logs.operations.MetadataCheckpointOperation)5 StorageMetadataCheckpointOperation (io.pravega.segmentstore.server.logs.operations.StorageMetadataCheckpointOperation)5 StreamSegmentAppendOperation (io.pravega.segmentstore.server.logs.operations.StreamSegmentAppendOperation)5 UpdateableSegmentMetadata (io.pravega.segmentstore.server.UpdateableSegmentMetadata)4 AtomicLong (java.util.concurrent.atomic.AtomicLong)4 Cleanup (lombok.Cleanup)4 SegmentProperties (io.pravega.segmentstore.contracts.SegmentProperties)3 MetadataBuilder (io.pravega.segmentstore.server.MetadataBuilder)3 MergeTransactionOperation (io.pravega.segmentstore.server.logs.operations.MergeTransactionOperation)3 SegmentOperation (io.pravega.segmentstore.server.logs.operations.SegmentOperation)3 ArrayList (java.util.ArrayList)3 StreamSegmentNotExistsException (io.pravega.segmentstore.contracts.StreamSegmentNotExistsException)2 ReadIndex (io.pravega.segmentstore.server.ReadIndex)2 SegmentMetadata (io.pravega.segmentstore.server.SegmentMetadata)2 TestDurableDataLogFactory (io.pravega.segmentstore.server.TestDurableDataLogFactory)2 Operation (io.pravega.segmentstore.server.logs.operations.Operation)2