use of io.pravega.segmentstore.contracts.StreamSegmentMergedException in project pravega by pravega.
the class ContainerMetadataUpdateTransactionTests method testPreProcessMergeSegment.
// endregion
// region MergeSegmentOperation
/**
* Tests the preProcess method with MergeTransactionOperations.
* Scenarios:
* * Recovery/non-recovery mode
* * Target StreamSegment is sealed
* * Target StreamSegment is a Transaction
* * Source StreamSegment is already merged
* * Source StreamSegment is not sealed
*/
@Test
public void testPreProcessMergeSegment() throws Exception {
UpdateableContainerMetadata metadata = createMetadata();
// When everything is OK (recovery mode).
MergeSegmentOperation recoveryMergeOp = createMerge();
metadata.enterRecoveryMode();
val txn1 = createUpdateTransaction(metadata);
AssertExtensions.assertThrows("preProcess(Merge) handled an operation with no Transaction StreamSegment Length set.", () -> txn1.preProcessOperation(createMerge()), ex -> ex instanceof MetadataUpdateException);
// In recovery mode, the updater does not set the length; it just validates that it has one.
recoveryMergeOp.setLength(metadata.getStreamSegmentMetadata(SEALED_SOURCE_ID).getLength());
txn1.preProcessOperation(recoveryMergeOp);
AssertExtensions.assertLessThan("Unexpected Target StreamSegmentOffset after call to preProcess in recovery mode.", 0, recoveryMergeOp.getStreamSegmentOffset());
checkNoSequenceNumberAssigned(recoveryMergeOp, "call to preProcess in recovery mode");
Assert.assertFalse("preProcess(Merge) seems to have changed the Updater internal state in recovery mode.", txn1.getStreamSegmentMetadata(SEALED_SOURCE_ID).isMerged());
Assert.assertFalse("preProcess(Merge) seems to have changed the metadata in recovery mode.", metadata.getStreamSegmentMetadata(SEALED_SOURCE_ID).isMerged());
// When everything is OK (non-recovery mode).
MergeSegmentOperation mergeOp = createMerge();
metadata.exitRecoveryMode();
val txn2 = createUpdateTransaction(metadata);
txn2.preProcessOperation(mergeOp);
Assert.assertEquals("Unexpected Transaction StreamSegmentLength after call to preProcess in non-recovery mode.", SEALED_SOURCE_LENGTH, mergeOp.getLength());
Assert.assertEquals("Unexpected Target StreamSegmentOffset after call to preProcess in non-recovery mode.", SEGMENT_LENGTH, mergeOp.getStreamSegmentOffset());
checkNoSequenceNumberAssigned(mergeOp, "call to preProcess in non-recovery mode");
Assert.assertFalse("preProcess(Merge) seems to have changed the Updater internal state in non-recovery mode.", txn2.getStreamSegmentMetadata(SEALED_SOURCE_ID).isMerged());
Assert.assertFalse("preProcess(Merge) seems to have changed the metadata in non-recovery mode.", metadata.getStreamSegmentMetadata(SEALED_SOURCE_ID).isMerged());
// When Target StreamSegment is sealed.
StreamSegmentSealOperation sealTargetOp = createSeal();
txn2.preProcessOperation(sealTargetOp);
txn2.acceptOperation(sealTargetOp);
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Merge) when Target StreamSegment is sealed.", () -> txn2.preProcessOperation(createMerge()), ex -> ex instanceof StreamSegmentSealedException);
// Rollback the seal
txn2.clear();
// When Transaction is not sealed.
MergeSegmentOperation mergeNonSealed = new MergeSegmentOperation(NOTSEALED_SOURCE_ID, SEGMENT_ID);
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Merge) when Transaction StreamSegment is not sealed.", () -> txn2.preProcessOperation(mergeNonSealed), ex -> ex instanceof StreamSegmentNotSealedException);
// When Transaction is already merged.
txn2.preProcessOperation(mergeOp);
txn2.acceptOperation(mergeOp);
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Merge) when Transaction StreamSegment is already merged (in transaction).", () -> txn2.preProcessOperation(createMerge()), ex -> ex instanceof StreamSegmentMergedException);
txn2.commit(metadata);
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Merge) when Transaction StreamSegment is already merged (in metadata).", () -> txn2.preProcessOperation(createMerge()), ex -> ex instanceof StreamSegmentMergedException);
}
use of io.pravega.segmentstore.contracts.StreamSegmentMergedException in project pravega by pravega.
the class StreamSegmentContainerTests method testConditionalTransactionOperations.
/**
* Test the createTransaction, append-to-Transaction, mergeTransaction methods with attribute updates.
*/
@Test
public void testConditionalTransactionOperations() throws Exception {
@Cleanup TestContext context = createContext();
context.container.startAsync().awaitRunning();
// 1. Create the StreamSegments.
ArrayList<String> segmentNames = createSegments(context);
HashMap<String, ArrayList<String>> transactionsBySegment = createTransactions(segmentNames, context);
activateAllSegments(segmentNames, context);
transactionsBySegment.values().forEach(s -> activateAllSegments(s, context));
// 2. Add some appends.
HashMap<String, Long> lengths = new HashMap<>();
HashMap<String, ByteArrayOutputStream> segmentContents = new HashMap<>();
appendToParentsAndTransactions(segmentNames, transactionsBySegment, lengths, segmentContents, context);
// 3. Correctly update attribute on parent Segments. Each source Segment will be initialized with a value and
// after the merge, that value should have been updated.
ArrayList<CompletableFuture<Void>> opFutures = new ArrayList<>();
for (Map.Entry<String, ArrayList<String>> e : transactionsBySegment.entrySet()) {
String parentName = e.getKey();
for (String transactionName : e.getValue()) {
opFutures.add(context.container.updateAttributes(parentName, AttributeUpdateCollection.from(new AttributeUpdate(AttributeId.fromUUID(UUID.nameUUIDFromBytes(transactionName.getBytes())), AttributeUpdateType.None, transactionName.hashCode())), TIMEOUT));
}
}
Futures.allOf(opFutures).get(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
// 4. Merge all the Transactions. Now this should work.
Futures.allOf(mergeTransactions(transactionsBySegment, lengths, segmentContents, context, true)).get(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
// 5. Add more appends (to the parent segments)
ArrayList<CompletableFuture<Long>> appendFutures = new ArrayList<>();
HashMap<String, CompletableFuture<Map<AttributeId, Long>>> getAttributeFutures = new HashMap<>();
for (int i = 0; i < 5; i++) {
for (String segmentName : segmentNames) {
RefCountByteArraySegment appendData = getAppendData(segmentName, APPENDS_PER_SEGMENT + i);
appendFutures.add(context.container.append(segmentName, appendData, null, TIMEOUT));
lengths.put(segmentName, lengths.getOrDefault(segmentName, 0L) + appendData.getLength());
recordAppend(segmentName, appendData, segmentContents, null);
// Verify that we can no longer append to Transaction.
for (String transactionName : transactionsBySegment.get(segmentName)) {
AssertExtensions.assertThrows("An append was allowed to a merged Transaction " + transactionName, context.container.append(transactionName, new ByteArraySegment("foo".getBytes()), null, TIMEOUT)::join, ex -> ex instanceof StreamSegmentMergedException || ex instanceof StreamSegmentNotExistsException);
getAttributeFutures.put(transactionName, context.container.getAttributes(segmentName, Collections.singletonList(AttributeId.fromUUID(UUID.nameUUIDFromBytes(transactionName.getBytes()))), true, TIMEOUT));
}
}
}
Futures.allOf(appendFutures).get(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
Futures.allOf(getAttributeFutures.values()).get(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
// 6. Verify their contents.
checkReadIndex(segmentContents, lengths, context);
// 7. Writer moving data to Storage.
waitForSegmentsInStorage(segmentNames, context).get(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
checkStorage(segmentContents, lengths, context);
// 8. Verify that the parent Segment contains the expected attributes updated.
for (Map.Entry<String, CompletableFuture<Map<AttributeId, Long>>> transactionAndAttribute : getAttributeFutures.entrySet()) {
Map<AttributeId, Long> segmentAttributeUpdated = transactionAndAttribute.getValue().join();
AttributeId transactionAttributeId = AttributeId.fromUUID(UUID.nameUUIDFromBytes(transactionAndAttribute.getKey().getBytes()));
// Conditional merges in mergeTransactions() update the attribute value to adding 1.
Assert.assertEquals(transactionAndAttribute.getKey().hashCode() + 1, segmentAttributeUpdated.get(transactionAttributeId).longValue());
}
context.container.stopAsync().awaitTerminated();
}
Aggregations