use of io.pravega.common.util.SequencedItemList in project pravega by pravega.
the class MemoryStateUpdaterTests method testProcess.
/**
* Tests the functionality of the process() method.
*/
@Test
public void testProcess() throws Exception {
int segmentCount = 10;
int operationCountPerType = 5;
// Add to MTL + Add to ReadIndex (append; beginMerge).
SequencedItemList<Operation> opLog = new SequencedItemList<>();
ArrayList<TestReadIndex.MethodInvocation> methodInvocations = new ArrayList<>();
TestReadIndex readIndex = new TestReadIndex(methodInvocations::add);
AtomicInteger flushCallbackCallCount = new AtomicInteger();
MemoryStateUpdater updater = new MemoryStateUpdater(opLog, readIndex, flushCallbackCallCount::incrementAndGet);
ArrayList<Operation> operations = populate(updater, segmentCount, operationCountPerType);
// Verify they were properly processed.
int triggerFutureCount = (int) methodInvocations.stream().filter(mi -> mi.methodName.equals(TestReadIndex.TRIGGER_FUTURE_READS)).count();
int addCount = methodInvocations.size() - triggerFutureCount;
Assert.assertEquals("Unexpected number of items added to ReadIndex.", operations.size() - segmentCount * operationCountPerType, addCount);
Assert.assertEquals("Unexpected number of calls to the ReadIndex triggerFutureReads method.", 1, triggerFutureCount);
Assert.assertEquals("Unexpected number of calls to the flushCallback provided in the constructor.", 1, flushCallbackCallCount.get());
// Verify add calls.
Iterator<Operation> logIterator = opLog.read(-1, operations.size());
int currentIndex = -1;
int currentReadIndex = -1;
while (logIterator.hasNext()) {
currentIndex++;
Operation expected = operations.get(currentIndex);
Operation actual = logIterator.next();
if (expected instanceof StorageOperation) {
currentReadIndex++;
TestReadIndex.MethodInvocation invokedMethod = methodInvocations.get(currentReadIndex);
if (expected instanceof StreamSegmentAppendOperation) {
Assert.assertTrue("StreamSegmentAppendOperation was not added as a CachedStreamSegmentAppendOperation to the Memory Log.", actual instanceof CachedStreamSegmentAppendOperation);
StreamSegmentAppendOperation appendOp = (StreamSegmentAppendOperation) expected;
Assert.assertEquals("Append with SeqNo " + expected.getSequenceNumber() + " was not added to the ReadIndex.", TestReadIndex.APPEND, invokedMethod.methodName);
Assert.assertEquals("Append with SeqNo " + expected.getSequenceNumber() + " was added to the ReadIndex with wrong arguments.", appendOp.getStreamSegmentId(), invokedMethod.args.get("streamSegmentId"));
Assert.assertEquals("Append with SeqNo " + expected.getSequenceNumber() + " was added to the ReadIndex with wrong arguments.", appendOp.getStreamSegmentOffset(), invokedMethod.args.get("offset"));
Assert.assertEquals("Append with SeqNo " + expected.getSequenceNumber() + " was added to the ReadIndex with wrong arguments.", appendOp.getData(), invokedMethod.args.get("data"));
} else if (expected instanceof MergeTransactionOperation) {
MergeTransactionOperation mergeOp = (MergeTransactionOperation) expected;
Assert.assertEquals("Merge with SeqNo " + expected.getSequenceNumber() + " was not added to the ReadIndex.", TestReadIndex.BEGIN_MERGE, invokedMethod.methodName);
Assert.assertEquals("Merge with SeqNo " + expected.getSequenceNumber() + " was added to the ReadIndex with wrong arguments.", mergeOp.getStreamSegmentId(), invokedMethod.args.get("targetStreamSegmentId"));
Assert.assertEquals("Merge with SeqNo " + expected.getSequenceNumber() + " was added to the ReadIndex with wrong arguments.", mergeOp.getStreamSegmentOffset(), invokedMethod.args.get("offset"));
Assert.assertEquals("Merge with SeqNo " + expected.getSequenceNumber() + " was added to the ReadIndex with wrong arguments.", mergeOp.getTransactionSegmentId(), invokedMethod.args.get("sourceStreamSegmentId"));
}
}
}
// Verify triggerFutureReads args.
@SuppressWarnings("unchecked") Collection<Long> triggerSegmentIds = (Collection<Long>) methodInvocations.stream().filter(mi -> mi.methodName.equals(TestReadIndex.TRIGGER_FUTURE_READS)).findFirst().get().args.get("streamSegmentIds");
val expectedSegmentIds = operations.stream().filter(op -> op instanceof SegmentOperation).map(op -> ((SegmentOperation) op).getStreamSegmentId()).collect(Collectors.toSet());
AssertExtensions.assertContainsSameElements("ReadIndex.triggerFutureReads() was called with the wrong set of StreamSegmentIds.", expectedSegmentIds, triggerSegmentIds);
// Test DataCorruptionException.
AssertExtensions.assertThrows("MemoryStateUpdater accepted an operation that was out of order.", // This does not have a SequenceNumber set, so it should trigger a DCE.
() -> updater.process(new MergeTransactionOperation(1, 2)), ex -> ex instanceof DataCorruptionException);
}
use of io.pravega.common.util.SequencedItemList in project pravega by pravega.
the class OperationProcessorTests method performLogOperationChecks.
private void performLogOperationChecks(Collection<OperationWithCompletion> operations, SequencedItemList<Operation> memoryLog, DurableDataLog dataLog, TruncationMarkerRepository truncationMarkers, int maxCount) throws Exception {
// Log Operation based checks
val successfulOps = operations.stream().filter(oc -> !oc.completion.isCompletedExceptionally()).map(oc -> oc.operation).filter(Operation::canSerialize).limit(maxCount).collect(Collectors.toList());
@Cleanup DataFrameReader<Operation> dataFrameReader = new DataFrameReader<>(dataLog, new OperationSerializer(), CONTAINER_ID);
long lastSeqNo = -1;
if (successfulOps.size() > 0) {
// Writing to the memory log is asynchronous and we don't have any callbacks to know when it was written to.
// We check periodically until the last item has been written.
await(() -> memoryLog.read(successfulOps.get(successfulOps.size() - 1).getSequenceNumber() - 1, 1).hasNext(), 10);
}
Iterator<Operation> memoryLogIterator = memoryLog.read(-1, operations.size() + 1);
OperationComparer memoryLogComparer = new OperationComparer(true);
for (Operation expectedOp : successfulOps) {
// Verify that the operations have been completed and assigned sequential Sequence Numbers.
AssertExtensions.assertGreaterThan("Operations were not assigned sequential Sequence Numbers.", lastSeqNo, expectedOp.getSequenceNumber());
lastSeqNo = expectedOp.getSequenceNumber();
// MemoryLog: verify that the operations match that of the expected list.
Assert.assertTrue("No more items left to read from MemoryLog. Expected: " + expectedOp, memoryLogIterator.hasNext());
// Use memoryLogComparer: we are actually expecting the same object here.
Operation actual = memoryLogIterator.next();
memoryLogComparer.assertEquals("Unexpected Operation in MemoryLog.", expectedOp, actual);
// DataLog: read back using DataFrameReader and verify the operations match that of the expected list.
DataFrameRecord<Operation> dataFrameRecord = dataFrameReader.getNext();
Assert.assertNotNull("No more items left to read from DataLog. Expected: " + expectedOp, dataFrameRecord);
// We are reading the raw operation from the DataFrame, so expect different objects (but same contents).
OperationComparer.DEFAULT.assertEquals(expectedOp, dataFrameRecord.getItem());
// Check truncation markers if this is the last Operation to be written.
LogAddress dataFrameAddress = truncationMarkers.getClosestTruncationMarker(expectedOp.getSequenceNumber());
if (dataFrameRecord.getLastFullDataFrameAddress() != null && dataFrameRecord.getLastFullDataFrameAddress().getSequence() != dataFrameRecord.getLastUsedDataFrameAddress().getSequence()) {
// This operation spans multiple DataFrames. The TruncationMarker should be set on the last DataFrame
// that ends with a part of it.
Assert.assertEquals("Unexpected truncation marker for Operation SeqNo " + expectedOp.getSequenceNumber() + " when it spans multiple DataFrames.", dataFrameRecord.getLastFullDataFrameAddress(), dataFrameAddress);
} else if (dataFrameRecord.isLastFrameEntry()) {
// The operation was the last one in the frame. This is a Truncation Marker.
Assert.assertEquals("Unexpected truncation marker for Operation SeqNo " + expectedOp.getSequenceNumber() + " when it is the last entry in a DataFrame.", dataFrameRecord.getLastUsedDataFrameAddress(), dataFrameAddress);
} else {
// The operation is not the last in the frame, and it doesn't span multiple frames either.
// There could be data after it that is not safe to truncate. The correct Truncation Marker is the
// same as the one for the previous operation.
LogAddress expectedTruncationMarker = truncationMarkers.getClosestTruncationMarker(expectedOp.getSequenceNumber() - 1);
Assert.assertEquals("Unexpected truncation marker for Operation SeqNo " + expectedOp.getSequenceNumber() + " when it is in the middle of a DataFrame.", expectedTruncationMarker, dataFrameAddress);
}
}
}
use of io.pravega.common.util.SequencedItemList in project pravega by pravega.
the class MemoryStateUpdaterTests method testRecoveryMode.
/**
* Tests the ability of the MemoryStateUpdater to delegate Enter/Exit recovery mode to the read index.
*/
@Test
public void testRecoveryMode() throws Exception {
// Check it's properly delegated to Read index.
SequencedItemList<Operation> opLog = new SequencedItemList<>();
ArrayList<TestReadIndex.MethodInvocation> methodInvocations = new ArrayList<>();
TestReadIndex readIndex = new TestReadIndex(methodInvocations::add);
MemoryStateUpdater updater = new MemoryStateUpdater(opLog, readIndex, Runnables.doNothing());
UpdateableContainerMetadata metadata1 = new MetadataBuilder(1).build();
updater.enterRecoveryMode(metadata1);
updater.exitRecoveryMode(true);
Assert.assertEquals("Unexpected number of method invocations.", 2, methodInvocations.size());
TestReadIndex.MethodInvocation enterRecovery = methodInvocations.get(0);
Assert.assertEquals("ReadIndex.enterRecoveryMode was not called when expected.", TestReadIndex.ENTER_RECOVERY_MODE, enterRecovery.methodName);
Assert.assertEquals("ReadIndex.enterRecoveryMode was called with the wrong arguments.", metadata1, enterRecovery.args.get("recoveryMetadataSource"));
TestReadIndex.MethodInvocation exitRecovery = methodInvocations.get(1);
Assert.assertEquals("ReadIndex.exitRecoveryMode was not called when expected.", TestReadIndex.EXIT_RECOVERY_MODE, exitRecovery.methodName);
Assert.assertEquals("ReadIndex.exitRecoveryMode was called with the wrong arguments.", true, exitRecovery.args.get("successfulRecovery"));
}
Aggregations