Search in sources :

Example 31 with AttributeUpdateCollection

use of io.pravega.segmentstore.contracts.AttributeUpdateCollection in project pravega by pravega.

the class StreamSegmentContainerTests method testConcurrentSegmentActivation.

/**
 * Tests the ability for the StreamSegmentContainer to handle concurrent actions on a Segment that it does not know
 * anything about, and handling the resulting concurrency.
 * Note: this is tested with a single segment. It could be tested with multiple segments, but different segments
 * are mostly independent of each other, so we would not be gaining much by doing so.
 */
@Test
public void testConcurrentSegmentActivation() throws Exception {
    final AttributeId attributeAccumulate = AttributeId.randomUUID();
    final long expectedAttributeValue = APPENDS_PER_SEGMENT + ATTRIBUTE_UPDATES_PER_SEGMENT;
    final int appendLength = 10;
    @Cleanup TestContext context = createContext();
    context.container.startAsync().awaitRunning();
    // 1. Create the StreamSegments.
    String segmentName = createSegments(context).get(0);
    // 2. Add some appends.
    List<CompletableFuture<Void>> opFutures = Collections.synchronizedList(new ArrayList<>());
    AtomicLong expectedLength = new AtomicLong();
    @Cleanup("shutdown") ExecutorService testExecutor = newScheduledThreadPool(Math.min(20, APPENDS_PER_SEGMENT), "testConcurrentSegmentActivation");
    val submitFutures = new ArrayList<Future<?>>();
    for (int i = 0; i < APPENDS_PER_SEGMENT; i++) {
        final byte fillValue = (byte) i;
        submitFutures.add(testExecutor.submit(() -> {
            val attributeUpdates = AttributeUpdateCollection.from(new AttributeUpdate(attributeAccumulate, AttributeUpdateType.Accumulate, 1));
            byte[] appendData = new byte[appendLength];
            Arrays.fill(appendData, (byte) (fillValue + 1));
            opFutures.add(Futures.toVoid(context.container.append(segmentName, new ByteArraySegment(appendData), attributeUpdates, TIMEOUT)));
            expectedLength.addAndGet(appendData.length);
        }));
    }
    // 2.1 Update the attribute.
    for (int i = 0; i < ATTRIBUTE_UPDATES_PER_SEGMENT; i++) {
        submitFutures.add(testExecutor.submit(() -> {
            AttributeUpdateCollection attributeUpdates = AttributeUpdateCollection.from(new AttributeUpdate(attributeAccumulate, AttributeUpdateType.Accumulate, 1));
            opFutures.add(context.container.updateAttributes(segmentName, attributeUpdates, TIMEOUT));
        }));
    }
    // Wait for the submittal of tasks to complete.
    submitFutures.forEach(this::await);
    // Now wait for all the appends to finish.
    Futures.allOf(opFutures).get(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
    // 3. getSegmentInfo: verify final state of the attribute.
    SegmentProperties sp = context.container.getStreamSegmentInfo(segmentName, TIMEOUT).join();
    Assert.assertEquals("Unexpected length for segment " + segmentName, expectedLength.get(), sp.getLength());
    Assert.assertFalse("Unexpected value for isDeleted for segment " + segmentName, sp.isDeleted());
    Assert.assertFalse("Unexpected value for isSealed for segment " + segmentName, sp.isDeleted());
    Assert.assertEquals("Unexpected Segment Type.", getSegmentType(segmentName), SegmentType.fromAttributes(sp.getAttributes()));
    // Verify all attribute values.
    Assert.assertEquals("Unexpected value for attribute " + attributeAccumulate + " for segment " + segmentName, expectedAttributeValue, (long) sp.getAttributes().getOrDefault(attributeAccumulate, Attributes.NULL_ATTRIBUTE_VALUE));
    checkActiveSegments(context.container, 1);
    // 4. Written data.
    waitForOperationsInReadIndex(context.container);
    byte[] actualData = new byte[(int) expectedLength.get()];
    int offset = 0;
    @Cleanup ReadResult readResult = context.container.read(segmentName, 0, actualData.length, TIMEOUT).join();
    while (readResult.hasNext()) {
        ReadResultEntry readEntry = readResult.next();
        BufferView readEntryContents = readEntry.getContent().join();
        AssertExtensions.assertLessThanOrEqual("Too much to read.", actualData.length, offset + actualData.length);
        readEntryContents.copyTo(ByteBuffer.wrap(actualData, offset, actualData.length));
        offset += actualData.length;
    }
    Assert.assertEquals("Unexpected number of bytes read.", actualData.length, offset);
    Assert.assertTrue("Unexpected number of bytes read (multiple of appendLength).", actualData.length % appendLength == 0);
    boolean[] observedValues = new boolean[APPENDS_PER_SEGMENT + 1];
    for (int i = 0; i < actualData.length; i += appendLength) {
        byte value = actualData[i];
        Assert.assertFalse("Append with value " + value + " was written multiple times.", observedValues[value]);
        observedValues[value] = true;
        for (int j = 1; j < appendLength; j++) {
            Assert.assertEquals("Append was not written atomically at offset " + (i + j), value, actualData[i + j]);
        }
    }
    // Verify all the appends made it (we purposefully did not write 0, since that's the default fill value in an array).
    Assert.assertFalse("Not expecting 0 as a value.", observedValues[0]);
    for (int i = 1; i < observedValues.length; i++) {
        Assert.assertTrue("Append with value " + i + " was not written.", observedValues[i]);
    }
    context.container.stopAsync().awaitTerminated();
}
Also used : lombok.val(lombok.val) AttributeUpdateCollection(io.pravega.segmentstore.contracts.AttributeUpdateCollection) AttributeUpdate(io.pravega.segmentstore.contracts.AttributeUpdate) DynamicAttributeUpdate(io.pravega.segmentstore.contracts.DynamicAttributeUpdate) ByteArraySegment(io.pravega.common.util.ByteArraySegment) AttributeId(io.pravega.segmentstore.contracts.AttributeId) ArrayList(java.util.ArrayList) ReadResult(io.pravega.segmentstore.contracts.ReadResult) Cleanup(lombok.Cleanup) CompletableFuture(java.util.concurrent.CompletableFuture) AtomicLong(java.util.concurrent.atomic.AtomicLong) BufferView(io.pravega.common.util.BufferView) ReadResultEntry(io.pravega.segmentstore.contracts.ReadResultEntry) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) ExecutorService(java.util.concurrent.ExecutorService) SegmentProperties(io.pravega.segmentstore.contracts.SegmentProperties) Test(org.junit.Test)

Example 32 with AttributeUpdateCollection

use of io.pravega.segmentstore.contracts.AttributeUpdateCollection in project pravega by pravega.

the class MetadataStoreTestBase method testCreateSegmentValidateAttributeIdLength.

/**
 * Verifies that {@link MetadataStore#createSegment} validates the {@link Attributes#ATTRIBUTE_ID_LENGTH} value, if
 * passed as an argument.
 */
@Test
public void testCreateSegmentValidateAttributeIdLength() {
    @Cleanup TestContext context = createTestContext();
    // Try to create a segment with invalid values.
    val s1 = getName(0);
    AssertExtensions.assertSuppliedFutureThrows("ATTRIBUTE_ID_LENGTH was accepted as negative.", () -> context.getMetadataStore().createSegment(s1, SegmentType.STREAM_SEGMENT, AttributeUpdateCollection.from(new AttributeUpdate(Attributes.ATTRIBUTE_ID_LENGTH, AttributeUpdateType.None, -1L)), TIMEOUT), ex -> ex instanceof IllegalArgumentException);
    AssertExtensions.assertSuppliedFutureThrows("ATTRIBUTE_ID_LENGTH was accepted with too high value.", () -> context.getMetadataStore().createSegment(s1, SegmentType.STREAM_SEGMENT, AttributeUpdateCollection.from(new AttributeUpdate(Attributes.ATTRIBUTE_ID_LENGTH, AttributeUpdateType.None, AttributeId.MAX_LENGTH + 1)), TIMEOUT), ex -> ex instanceof IllegalArgumentException);
    // Create a segment with valid values.
    val validAttributes = AttributeUpdateCollection.from(new AttributeUpdate(Attributes.ATTRIBUTE_ID_LENGTH, AttributeUpdateType.None, AttributeId.MAX_LENGTH / 2));
    context.getMetadataStore().createSegment(s1, SegmentType.STREAM_SEGMENT, validAttributes, TIMEOUT).join();
    assertSegmentCreated(s1, SegmentType.STREAM_SEGMENT, validAttributes, context);
    // Now verify that it also works if the attribute is not set.
    val noAttributes = new AttributeUpdateCollection();
    val s2 = getName(1);
    context.getMetadataStore().createSegment(s2, SegmentType.STREAM_SEGMENT, noAttributes, TIMEOUT).join();
    assertSegmentCreated(s2, SegmentType.STREAM_SEGMENT, noAttributes, context);
}
Also used : lombok.val(lombok.val) AttributeUpdateCollection(io.pravega.segmentstore.contracts.AttributeUpdateCollection) AttributeUpdate(io.pravega.segmentstore.contracts.AttributeUpdate) Cleanup(lombok.Cleanup) Test(org.junit.Test)

Example 33 with AttributeUpdateCollection

use of io.pravega.segmentstore.contracts.AttributeUpdateCollection in project pravega by pravega.

the class ContainerMetadataUpdateTransactionTests method testWithAttributes.

private void testWithAttributes(Function<AttributeUpdateCollection, Operation> createOperation) throws Exception {
    final AttributeId attributeNoUpdate = AttributeId.randomUUID();
    final AttributeId attributeAccumulate = AttributeId.randomUUID();
    final AttributeId attributeReplace = AttributeId.randomUUID();
    final AttributeId attributeReplaceIfGreater = AttributeId.randomUUID();
    final AttributeId attributeReplaceIfEquals = AttributeId.randomUUID();
    UpdateableContainerMetadata metadata = createMetadata();
    val txn = createUpdateTransaction(metadata);
    // Update #1.
    AttributeUpdateCollection attributeUpdates = new AttributeUpdateCollection();
    // Initial add, so it's ok.
    attributeUpdates.add(new AttributeUpdate(attributeNoUpdate, AttributeUpdateType.None, 1));
    attributeUpdates.add(new AttributeUpdate(attributeAccumulate, AttributeUpdateType.Accumulate, 1));
    attributeUpdates.add(new AttributeUpdate(attributeReplace, AttributeUpdateType.Replace, 1));
    attributeUpdates.add(new AttributeUpdate(attributeReplaceIfGreater, AttributeUpdateType.ReplaceIfGreater, 1));
    // Need to initialize to something.
    attributeUpdates.add(new AttributeUpdate(attributeReplaceIfEquals, AttributeUpdateType.Replace, 1));
    val expectedValues = attributeUpdates.stream().collect(Collectors.toMap(AttributeUpdate::getAttributeId, AttributeUpdate::getValue));
    expectedValues.put(Attributes.ATTRIBUTE_SEGMENT_TYPE, DEFAULT_TYPE.getValue());
    Operation op = createOperation.apply(attributeUpdates);
    txn.preProcessOperation(op);
    txn.acceptOperation(op);
    // Verify that the AttributeUpdates still have the same values (there was nothing there prior) and that the updater
    // has internalized the attribute updates.
    verifyAttributeUpdates("after acceptOperation (1)", txn, attributeUpdates, expectedValues);
    // Update #2: update all attributes that can be updated.
    attributeUpdates.clear();
    // 1 + 1 = 2
    attributeUpdates.add(new AttributeUpdate(attributeAccumulate, AttributeUpdateType.Accumulate, 1));
    attributeUpdates.add(new AttributeUpdate(attributeReplace, AttributeUpdateType.Replace, 2));
    attributeUpdates.add(new AttributeUpdate(attributeReplaceIfGreater, AttributeUpdateType.ReplaceIfGreater, 2));
    attributeUpdates.add(new AttributeUpdate(attributeReplaceIfEquals, AttributeUpdateType.ReplaceIfEquals, 2, 1));
    expectedValues.put(attributeAccumulate, 2L);
    expectedValues.put(attributeReplace, 2L);
    expectedValues.put(attributeReplaceIfGreater, 2L);
    expectedValues.put(attributeReplaceIfEquals, 2L);
    op = createOperation.apply(attributeUpdates);
    txn.preProcessOperation(op);
    txn.acceptOperation(op);
    // This is still in the transaction, so we need to add it for comparison sake.
    attributeUpdates.add(new AttributeUpdate(attributeNoUpdate, AttributeUpdateType.None, 1));
    verifyAttributeUpdates("after acceptOperation (2)", txn, attributeUpdates, expectedValues);
    // Update #3: after commit, verify that attributes are committed when they need to.
    val previousAcceptedValues = new HashMap<>(expectedValues);
    txn.commit(metadata);
    attributeUpdates.clear();
    // 2 + 1 = 3
    attributeUpdates.add(new AttributeUpdate(attributeAccumulate, AttributeUpdateType.Accumulate, 1));
    attributeUpdates.add(new AttributeUpdate(attributeReplace, AttributeUpdateType.Replace, 3));
    attributeUpdates.add(new AttributeUpdate(attributeReplaceIfGreater, AttributeUpdateType.ReplaceIfGreater, 3));
    attributeUpdates.add(new AttributeUpdate(attributeReplaceIfEquals, AttributeUpdateType.ReplaceIfEquals, 3, 2));
    expectedValues.put(attributeAccumulate, 3L);
    expectedValues.put(attributeReplace, 3L);
    expectedValues.put(attributeReplaceIfGreater, 3L);
    expectedValues.put(attributeReplaceIfEquals, 3L);
    op = createOperation.apply(attributeUpdates);
    txn.preProcessOperation(op);
    txn.acceptOperation(op);
    SegmentMetadataComparer.assertSameAttributes("Unexpected attributes in segment metadata after commit+acceptOperation, but prior to second commit.", previousAcceptedValues, metadata.getStreamSegmentMetadata(SEGMENT_ID));
    verifyAttributeUpdates("after commit+acceptOperation", txn, attributeUpdates, expectedValues);
    // Final step: commit Append #3, and verify final segment metadata.
    txn.commit(metadata);
    SegmentMetadataComparer.assertSameAttributes("Unexpected attributes in segment metadata after final commit.", expectedValues, metadata.getStreamSegmentMetadata(SEGMENT_ID));
}
Also used : lombok.val(lombok.val) AttributeUpdateCollection(io.pravega.segmentstore.contracts.AttributeUpdateCollection) AttributeUpdate(io.pravega.segmentstore.contracts.AttributeUpdate) DynamicAttributeUpdate(io.pravega.segmentstore.contracts.DynamicAttributeUpdate) HashMap(java.util.HashMap) AttributeId(io.pravega.segmentstore.contracts.AttributeId) UpdateableContainerMetadata(io.pravega.segmentstore.server.UpdateableContainerMetadata) StorageMetadataCheckpointOperation(io.pravega.segmentstore.server.logs.operations.StorageMetadataCheckpointOperation) MergeSegmentOperation(io.pravega.segmentstore.server.logs.operations.MergeSegmentOperation) Operation(io.pravega.segmentstore.server.logs.operations.Operation) StreamSegmentTruncateOperation(io.pravega.segmentstore.server.logs.operations.StreamSegmentTruncateOperation) StreamSegmentMapOperation(io.pravega.segmentstore.server.logs.operations.StreamSegmentMapOperation) UpdateAttributesOperation(io.pravega.segmentstore.server.logs.operations.UpdateAttributesOperation) MetadataCheckpointOperation(io.pravega.segmentstore.server.logs.operations.MetadataCheckpointOperation) StorageOperation(io.pravega.segmentstore.server.logs.operations.StorageOperation) StreamSegmentAppendOperation(io.pravega.segmentstore.server.logs.operations.StreamSegmentAppendOperation) DeleteSegmentOperation(io.pravega.segmentstore.server.logs.operations.DeleteSegmentOperation) StreamSegmentSealOperation(io.pravega.segmentstore.server.logs.operations.StreamSegmentSealOperation)

Aggregations

AttributeUpdateCollection (io.pravega.segmentstore.contracts.AttributeUpdateCollection)33 AttributeUpdate (io.pravega.segmentstore.contracts.AttributeUpdate)28 lombok.val (lombok.val)23 AttributeId (io.pravega.segmentstore.contracts.AttributeId)18 ArrayList (java.util.ArrayList)17 HashMap (java.util.HashMap)16 SegmentProperties (io.pravega.segmentstore.contracts.SegmentProperties)15 CompletableFuture (java.util.concurrent.CompletableFuture)15 AttributeUpdateType (io.pravega.segmentstore.contracts.AttributeUpdateType)14 Test (org.junit.Test)14 Map (java.util.Map)13 Attributes (io.pravega.segmentstore.contracts.Attributes)12 Exceptions (io.pravega.common.Exceptions)11 Futures (io.pravega.common.concurrent.Futures)11 BufferView (io.pravega.common.util.BufferView)11 BadAttributeUpdateException (io.pravega.segmentstore.contracts.BadAttributeUpdateException)11 SegmentType (io.pravega.segmentstore.contracts.SegmentType)11 UpdateableSegmentMetadata (io.pravega.segmentstore.server.UpdateableSegmentMetadata)11 Operation (io.pravega.segmentstore.server.logs.operations.Operation)11 StreamSegmentSealOperation (io.pravega.segmentstore.server.logs.operations.StreamSegmentSealOperation)11