use of io.pravega.segmentstore.server.logs.operations.StreamSegmentSealOperation in project pravega by pravega.
the class DataRecoveryTest method testDurableLogRepairCommandExpectedLogOutput.
@Test
public void testDurableLogRepairCommandExpectedLogOutput() throws Exception {
int instanceId = 0;
int bookieCount = 3;
int containerCount = 1;
@Cleanup TestUtils.PravegaRunner pravegaRunner = new TestUtils.PravegaRunner(bookieCount, containerCount);
pravegaRunner.startBookKeeperRunner(instanceId);
val bkConfig = BookKeeperConfig.builder().with(BookKeeperConfig.ZK_ADDRESS, "localhost:" + pravegaRunner.getBookKeeperRunner().getBkPort()).with(BookKeeperConfig.BK_LEDGER_PATH, pravegaRunner.getBookKeeperRunner().getLedgerPath()).with(BookKeeperConfig.ZK_METADATA_PATH, pravegaRunner.getBookKeeperRunner().getLogMetaNamespace()).with(BookKeeperConfig.BK_ENSEMBLE_SIZE, 1).with(BookKeeperConfig.BK_WRITE_QUORUM_SIZE, 1).with(BookKeeperConfig.BK_ACK_QUORUM_SIZE, 1).build();
this.factory = new BookKeeperLogFactory(bkConfig, pravegaRunner.getBookKeeperRunner().getZkClient().get(), this.executorService());
pravegaRunner.startControllerAndSegmentStore(this.storageFactory, this.factory);
String streamName = "testDataRecoveryCommand";
TestUtils.createScopeStream(pravegaRunner.getControllerRunner().getController(), SCOPE, streamName, config);
try (val clientRunner = new TestUtils.ClientRunner(pravegaRunner.getControllerRunner(), SCOPE)) {
// Write events to the streams.
TestUtils.writeEvents(streamName, clientRunner.getClientFactory());
}
// Shut down services, we assume that the cluster is in very bad shape in this test.
pravegaRunner.shutDownControllerRunner();
pravegaRunner.shutDownSegmentStoreRunner();
// set Pravega properties for the test
STATE.set(new AdminCommandState());
Properties pravegaProperties = new Properties();
pravegaProperties.setProperty("pravegaservice.container.count", "1");
pravegaProperties.setProperty("pravegaservice.storage.impl.name", "FILESYSTEM");
pravegaProperties.setProperty("pravegaservice.storage.layout", "ROLLING_STORAGE");
pravegaProperties.setProperty("pravegaservice.zk.connect.uri", "localhost:" + pravegaRunner.getBookKeeperRunner().getBkPort());
pravegaProperties.setProperty("bookkeeper.ledger.path", pravegaRunner.getBookKeeperRunner().getLedgerPath());
pravegaProperties.setProperty("bookkeeper.zk.metadata.path", pravegaRunner.getBookKeeperRunner().getLogMetaNamespace());
pravegaProperties.setProperty("pravegaservice.clusterName", "pravega0");
pravegaProperties.setProperty("filesystem.root", this.baseDir.getAbsolutePath());
STATE.get().getConfigBuilder().include(pravegaProperties);
// Execute basic command workflow for repairing DurableLog.
CommandArgs args = new CommandArgs(List.of("0"), STATE.get());
DurableDataLogRepairCommand command = Mockito.spy(new DurableDataLogRepairCommand(args));
this.factory = new BookKeeperLogFactory(bkConfig, pravegaRunner.getBookKeeperRunner().getZkClient().get(), this.executorService());
this.factory.initialize();
// First, keep all the Operations of Container 0 in this list, so we can compare with the modified one.
List<Operation> originalOperations = new ArrayList<>();
@Cleanup DebugDurableDataLogWrapper wrapper = this.factory.createDebugLogWrapper(0);
command.readDurableDataLogWithCustomCallback((op, entry) -> originalOperations.add(op), 0, wrapper.asReadOnly());
// Disable Original Log first.
System.setIn(new ByteArrayInputStream("yes".getBytes()));
TestUtils.executeCommand("bk disable 0", STATE.get());
// Second, add 2 operations, delete 1 operation, replace 1 operation.
Mockito.doReturn(true).doReturn(true).doReturn(false).doReturn(true).doReturn(true).doReturn(false).doReturn(false).doReturn(true).when(command).confirmContinue();
Mockito.doReturn(900L).doReturn(901L).doReturn(901L).doReturn(1L).doReturn(123L).doReturn(2L).doReturn(123L).doReturn(903L).doReturn(3L).doReturn(123L).doReturn(905L).doReturn(4L).doReturn(123L).when(command).getLongUserInput(Mockito.any());
Mockito.doReturn("delete").doReturn("add").doReturn("DeleteSegmentOperation").doReturn("DeleteSegmentOperation").doReturn("replace").doReturn("DeleteSegmentOperation").doReturn("add").doReturn("StreamSegmentSealOperation").when(command).getStringUserInput(Mockito.any());
command.execute();
List<Operation> originalOperationsEdited = new ArrayList<>();
@Cleanup DebugDurableDataLogWrapper wrapperEdited = this.factory.createDebugLogWrapper(0);
command.readDurableDataLogWithCustomCallback((op, entry) -> originalOperationsEdited.add(op), 0, wrapperEdited.asReadOnly());
// OP-905 (now 907)
for (int i = 899; i < 910; i++) {
// Sequence numbers will defer between the original and edited logs. To do equality comparisons between
// Operations in both logs, reset the sequence numbers (other fields should be the same).
originalOperations.get(i).resetSequenceNumber(0);
originalOperationsEdited.get(i).resetSequenceNumber(0);
}
Assert.assertNotEquals(originalOperations.get(899), originalOperationsEdited.get(899));
Assert.assertTrue(originalOperationsEdited.get(899) instanceof DeleteSegmentOperation);
Assert.assertTrue(originalOperationsEdited.get(900) instanceof DeleteSegmentOperation);
Assert.assertEquals(originalOperations.get(900).toString(), originalOperationsEdited.get(901).toString());
Assert.assertEquals(originalOperations.get(901).toString(), originalOperationsEdited.get(902).toString());
Assert.assertTrue(originalOperationsEdited.get(903) instanceof DeleteSegmentOperation);
Assert.assertEquals(originalOperations.get(903).toString(), originalOperationsEdited.get(904).toString());
Assert.assertTrue(originalOperationsEdited.get(905) instanceof StreamSegmentSealOperation);
Assert.assertEquals(originalOperations.get(904).toString(), originalOperationsEdited.get(906).toString());
this.factory.close();
}
use of io.pravega.segmentstore.server.logs.operations.StreamSegmentSealOperation in project pravega by pravega.
the class StreamSegmentContainerTests method testForSegmentPriority.
/**
* Tests {@link StreamSegmentContainer#forSegment(String, OperationPriority, Duration)}.
*/
@Test
public void testForSegmentPriority() throws Exception {
val segmentName = "Test";
@Cleanup val context = new TestContext(DEFAULT_CONFIG, NO_TRUNCATIONS_DURABLE_LOG_CONFIG, INFREQUENT_FLUSH_WRITER_CONFIG, null);
val durableLog = new AtomicReference<OperationLog>();
val durableLogFactory = new WatchableOperationLogFactory(context.operationLogFactory, durableLog::set);
@Cleanup val container = new StreamSegmentContainer(CONTAINER_ID, DEFAULT_CONFIG, durableLogFactory, context.readIndexFactory, context.attributeIndexFactory, new NoOpWriterFactory(), context.storageFactory, context.getDefaultExtensions(), executorService());
container.startAsync().awaitRunning();
container.createStreamSegment(segmentName, SegmentType.STREAM_SEGMENT, null, TIMEOUT).get(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
// Create a few operations using the forSegment with desired priority.
val s1 = container.forSegment(segmentName, OperationPriority.Critical, TIMEOUT).get(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
val futures = new ArrayList<CompletableFuture<Void>>();
futures.add(Futures.toVoid(s1.append(new ByteArraySegment(new byte[1]), null, TIMEOUT)));
futures.add(s1.updateAttributes(AttributeUpdateCollection.from(new AttributeUpdate(AttributeId.randomUUID(), AttributeUpdateType.Replace, 1)), TIMEOUT));
futures.add(s1.truncate(1, TIMEOUT));
futures.add(Futures.toVoid(s1.seal(TIMEOUT)));
Futures.allOf(futures).get(TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
// Await all operations to be added to the durable log, then fetch them all. We stop when we encounter the Seal we just added.
val ops = readDurableLog(durableLog.get(), op -> op instanceof StreamSegmentSealOperation);
// For those operations that we do care about, verify they have the right priority.
int count = 0;
for (val op : ops) {
if (op instanceof SegmentOperation && ((SegmentOperation) op).getStreamSegmentId() == s1.getSegmentId()) {
count++;
Assert.assertEquals("Unexpected priority for " + op, OperationPriority.Critical, op.getDesiredPriority());
}
}
AssertExtensions.assertGreaterThan("Expected at least one operation to be verified.", 0, count);
}
use of io.pravega.segmentstore.server.logs.operations.StreamSegmentSealOperation in project pravega by pravega.
the class DurableLogTests method testRecoveryWithDisabledDataLog.
/**
* Verifies the ability of hte DurableLog to recover (delayed start) using a disabled DurableDataLog. This verifies
* the ability to shut down correctly while still waiting for the DataLog to become enabled as well as detecting that
* it did become enabled and then resume normal operations.
*/
@Test
public void testRecoveryWithDisabledDataLog() throws Exception {
int streamSegmentCount = 50;
int appendsPerStreamSegment = 20;
AtomicReference<TestDurableDataLog> dataLog = new AtomicReference<>();
@Cleanup TestDurableDataLogFactory dataLogFactory = new TestDurableDataLogFactory(new InMemoryDurableDataLogFactory(MAX_DATA_LOG_APPEND_SIZE, executorService()), dataLog::set);
@Cleanup Storage storage = InMemoryStorageFactory.newStorage(executorService());
storage.initialize(1);
@Cleanup CacheStorage cacheStorage = new DirectMemoryCache(Integer.MAX_VALUE);
@Cleanup CacheManager cacheManager = new CacheManager(CachePolicy.INFINITE, cacheStorage, executorService());
// Write some data to the log. We'll read it later.
Set<Long> streamSegmentIds;
List<Operation> originalOperations;
List<OperationWithCompletion> completionFutures;
UpdateableContainerMetadata metadata = new MetadataBuilder(CONTAINER_ID).build();
dataLog.set(null);
try (ReadIndex readIndex = new ContainerReadIndex(DEFAULT_READ_INDEX_CONFIG, metadata, storage, cacheManager, executorService());
DurableLog durableLog = new DurableLog(ContainerSetup.defaultDurableLogConfig(), metadata, dataLogFactory, readIndex, executorService())) {
// DurableLog should start properly.
durableLog.startAsync().awaitRunning();
streamSegmentIds = createStreamSegmentsWithOperations(streamSegmentCount, durableLog);
List<Operation> operations = generateOperations(streamSegmentIds, new HashMap<>(), appendsPerStreamSegment, METADATA_CHECKPOINT_EVERY, false, false);
completionFutures = processOperations(operations, durableLog);
OperationWithCompletion.allOf(completionFutures).join();
originalOperations = readUpToSequenceNumber(durableLog, metadata.getOperationSequenceNumber());
}
// Disable the DurableDataLog. This requires us to initialize the log, then disable it.
metadata = new MetadataBuilder(CONTAINER_ID).build();
dataLog.set(null);
try (ReadIndex readIndex = new ContainerReadIndex(DEFAULT_READ_INDEX_CONFIG, metadata, storage, cacheManager, executorService());
DurableLog durableLog = new DurableLog(ContainerSetup.defaultDurableLogConfig(), metadata, dataLogFactory, readIndex, executorService())) {
// DurableLog should start properly.
durableLog.startAsync().awaitRunning();
CompletableFuture<Void> online = durableLog.awaitOnline();
Assert.assertTrue("awaitOnline() returned an incomplete future.", Futures.isSuccessful(online));
Assert.assertFalse("Not expecting an offline DurableLog.", durableLog.isOffline());
dataLog.get().disable();
}
// Verify that the DurableLog starts properly and that all operations throw appropriate exceptions.
try (ReadIndex readIndex = new ContainerReadIndex(DEFAULT_READ_INDEX_CONFIG, metadata, storage, cacheManager, executorService());
DurableLog durableLog = new DurableLog(ContainerSetup.defaultDurableLogConfig(), metadata, dataLogFactory, readIndex, executorService())) {
// DurableLog should start properly.
durableLog.startAsync().awaitRunning();
CompletableFuture<Void> online = durableLog.awaitOnline();
Assert.assertFalse("awaitOnline() returned a completed future.", online.isDone());
Assert.assertTrue("Expecting an offline DurableLog.", durableLog.isOffline());
// Verify all operations fail with the right exception.
AssertExtensions.assertSuppliedFutureThrows("add() did not fail with the right exception when offline.", () -> durableLog.add(new StreamSegmentSealOperation(123), OperationPriority.Normal, TIMEOUT), ex -> ex instanceof ContainerOfflineException);
AssertExtensions.assertSuppliedFutureThrows("read() did not fail with the right exception when offline.", () -> durableLog.read(1, TIMEOUT), ex -> ex instanceof ContainerOfflineException);
AssertExtensions.assertSuppliedFutureThrows("truncate() did not fail with the right exception when offline.", () -> durableLog.truncate(0, TIMEOUT), ex -> ex instanceof ContainerOfflineException);
// Verify we can also shut it down properly from this state.
durableLog.stopAsync().awaitTerminated();
Assert.assertTrue("awaitOnline() returned future did not fail when DurableLog shut down.", online.isCompletedExceptionally());
}
// Verify that, when the DurableDataLog becomes enabled, the DurableLog can pick up the change and resume normal operations.
// Verify that the DurableLog starts properly and that all operations throw appropriate exceptions.
dataLog.set(null);
try (ReadIndex readIndex = new ContainerReadIndex(DEFAULT_READ_INDEX_CONFIG, metadata, storage, cacheManager, executorService());
DurableLog durableLog = new DurableLog(ContainerSetup.defaultDurableLogConfig(), metadata, dataLogFactory, readIndex, executorService())) {
// DurableLog should start properly.
durableLog.startAsync().awaitRunning();
CompletableFuture<Void> online = durableLog.awaitOnline();
Assert.assertFalse("awaitOnline() returned a completed future.", online.isDone());
// Enable the underlying data log and await for recovery to finish.
dataLog.get().enable();
online.get(START_RETRY_DELAY_MILLIS * 100, TimeUnit.MILLISECONDS);
Assert.assertFalse("Not expecting an offline DurableLog after re-enabling.", durableLog.isOffline());
// Verify we can still read the data that we wrote before the DataLog was disabled.
List<Operation> recoveredOperations = readUpToSequenceNumber(durableLog, metadata.getOperationSequenceNumber());
assertRecoveredOperationsMatch(originalOperations, recoveredOperations);
performMetadataChecks(streamSegmentIds, new HashSet<>(), new HashMap<>(), completionFutures, metadata, false, false);
performReadIndexChecks(completionFutures, readIndex);
// Stop the processor.
durableLog.stopAsync().awaitTerminated();
}
}
use of io.pravega.segmentstore.server.logs.operations.StreamSegmentSealOperation in project pravega by pravega.
the class ContainerMetadataUpdateTransactionTests method testPreProcessStreamSegmentAppend.
// region StreamSegmentAppendOperation
/**
* Tests the preProcess method with StreamSegmentAppend operations.
* Scenarios:
* * Recovery Mode
* * Non-recovery mode
* * StreamSegment is Merged (both in-transaction and in-metadata)
* * StreamSegment is Sealed (both in-transaction and in-metadata)
*/
@Test
public void testPreProcessStreamSegmentAppend() throws Exception {
val metadata = createMetadata();
StreamSegmentAppendOperation appendOp = createAppendNoOffset();
// When everything is OK (in recovery mode) - nothing should change.
metadata.enterRecoveryMode();
val txn1 = createUpdateTransaction(metadata);
txn1.preProcessOperation(appendOp);
AssertExtensions.assertLessThan("Unexpected StreamSegmentOffset after call to preProcess in recovery mode.", 0, appendOp.getStreamSegmentOffset());
checkNoSequenceNumberAssigned(appendOp, "call to preProcess in recovery mode");
Assert.assertEquals("preProcess(Append) seems to have changed the Updater internal state in recovery mode.", SEGMENT_LENGTH, txn1.getStreamSegmentMetadata(SEGMENT_ID).getLength());
Assert.assertEquals("preProcess(Append) seems to have changed the metadata in recovery mode.", SEGMENT_LENGTH, metadata.getStreamSegmentMetadata(SEGMENT_ID).getLength());
// When everything is OK (no recovery mode).
metadata.exitRecoveryMode();
val txn2 = createUpdateTransaction(metadata);
txn2.preProcessOperation(appendOp);
Assert.assertEquals("Unexpected StreamSegmentOffset after call to preProcess in non-recovery mode.", SEGMENT_LENGTH, appendOp.getStreamSegmentOffset());
checkNoSequenceNumberAssigned(appendOp, "call to preProcess in non-recovery mode");
Assert.assertEquals("preProcess(Append) seems to have changed the Updater internal state.", SEGMENT_LENGTH, txn2.getStreamSegmentMetadata(SEGMENT_ID).getLength());
Assert.assertEquals("preProcess(Append) seems to have changed the metadata.", SEGMENT_LENGTH, metadata.getStreamSegmentMetadata(SEGMENT_ID).getLength());
// When StreamSegment is merged (via transaction).
StreamSegmentAppendOperation transactionAppendOp = new StreamSegmentAppendOperation(SEALED_SOURCE_ID, DEFAULT_APPEND_DATA, null);
MergeSegmentOperation mergeOp = createMerge();
txn2.preProcessOperation(mergeOp);
txn2.acceptOperation(mergeOp);
Assert.assertFalse("Transaction should not be merged in metadata (yet).", metadata.getStreamSegmentMetadata(SEALED_SOURCE_ID).isMerged());
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Append) when Segment is merged (in transaction).", () -> txn2.preProcessOperation(transactionAppendOp), ex -> ex instanceof StreamSegmentMergedException);
// When StreamSegment is merged (via metadata).
txn2.commit(metadata);
Assert.assertTrue("Transaction should have been merged in metadata.", metadata.getStreamSegmentMetadata(SEALED_SOURCE_ID).isMerged());
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Append) when Segment is merged (in metadata).", () -> txn2.preProcessOperation(transactionAppendOp), ex -> ex instanceof StreamSegmentMergedException);
// When StreamSegment is sealed (via transaction).
StreamSegmentSealOperation sealOp = createSeal();
txn2.preProcessOperation(sealOp);
txn2.acceptOperation(sealOp);
Assert.assertFalse("StreamSegment should not be sealed in metadata (yet).", metadata.getStreamSegmentMetadata(SEGMENT_ID).isSealed());
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Append) when Segment is sealed (in transaction).", () -> txn2.preProcessOperation(createAppendNoOffset()), ex -> ex instanceof StreamSegmentSealedException);
// When StreamSegment is sealed (via metadata).
txn2.commit(metadata);
Assert.assertTrue("StreamSegment should have been sealed in metadata.", metadata.getStreamSegmentMetadata(SEGMENT_ID).isSealed());
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Append) when Segment is sealed (in metadata).", () -> txn2.preProcessOperation(createAppendNoOffset()), ex -> ex instanceof StreamSegmentSealedException);
}
use of io.pravega.segmentstore.server.logs.operations.StreamSegmentSealOperation in project pravega by pravega.
the class ContainerMetadataUpdateTransactionTests method testPreProcessStreamSegmentSeal.
// endregion
// region StreamSegmentSealOperation
/**
* Tests the preProcess method with StreamSegmentSeal operations.
* Scenarios:
* * Recovery Mode
* * Non-recovery mode
* * StreamSegment is Merged (both in-transaction and in-metadata)
* * StreamSegment is Sealed (both in-transaction and in-metadata)
*/
@Test
public void testPreProcessStreamSegmentSeal() throws Exception {
UpdateableContainerMetadata metadata = createMetadata();
StreamSegmentSealOperation sealOp = createSeal();
// When everything is OK (in recovery mode) - nothing should change.
metadata.enterRecoveryMode();
val txn1 = createUpdateTransaction(metadata);
txn1.preProcessOperation(sealOp);
AssertExtensions.assertLessThan("Unexpected StreamSegmentLength after call to preProcess in recovery mode.", 0, sealOp.getStreamSegmentOffset());
checkNoSequenceNumberAssigned(sealOp, "call to preProcess in recovery mode");
Assert.assertFalse("preProcess(Seal) seems to have changed the Updater internal state in recovery mode.", txn1.getStreamSegmentMetadata(SEGMENT_ID).isSealed());
Assert.assertFalse("preProcess(Seal) seems to have changed the metadata in recovery mode.", metadata.getStreamSegmentMetadata(SEGMENT_ID).isSealed());
// When everything is OK (no recovery mode).
metadata.exitRecoveryMode();
val txn2 = createUpdateTransaction(metadata);
txn2.preProcessOperation(sealOp);
Assert.assertEquals("Unexpected StreamSegmentLength after call to preProcess in non-recovery mode.", SEGMENT_LENGTH, sealOp.getStreamSegmentOffset());
checkNoSequenceNumberAssigned(sealOp, "call to preProcess in non-recovery mode");
Assert.assertFalse("preProcess(Seal) seems to have changed the Updater internal state.", txn2.getStreamSegmentMetadata(SEGMENT_ID).isSealed());
Assert.assertFalse("preProcess(Seal) seems to have changed the metadata.", metadata.getStreamSegmentMetadata(SEGMENT_ID).isSealed());
// When StreamSegment is merged (via transaction).
StreamSegmentSealOperation transactionSealOp = new StreamSegmentSealOperation(SEALED_SOURCE_ID);
MergeSegmentOperation mergeOp = createMerge();
txn2.preProcessOperation(mergeOp);
txn2.acceptOperation(mergeOp);
Assert.assertFalse("Transaction should not be merged in metadata (yet).", metadata.getStreamSegmentMetadata(SEALED_SOURCE_ID).isMerged());
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Seal) when Segment is merged (in transaction).", () -> txn2.preProcessOperation(transactionSealOp), ex -> ex instanceof StreamSegmentMergedException);
// When StreamSegment is merged (via metadata).
txn2.commit(metadata);
Assert.assertTrue("Transaction should have been merged in metadata.", metadata.getStreamSegmentMetadata(SEALED_SOURCE_ID).isMerged());
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Seal) when Segment is merged (in metadata).", () -> txn2.preProcessOperation(transactionSealOp), ex -> ex instanceof StreamSegmentMergedException);
// When StreamSegment is sealed (via transaction).
txn2.acceptOperation(sealOp);
Assert.assertFalse("StreamSegment should not be sealed in metadata (yet).", metadata.getStreamSegmentMetadata(SEGMENT_ID).isSealed());
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Seal) when Segment is sealed (in transaction).", () -> txn2.preProcessOperation(createSeal()), ex -> ex instanceof StreamSegmentSealedException);
// When StreamSegment is sealed (via metadata).
txn2.commit(metadata);
Assert.assertTrue("StreamSegment should have been sealed in metadata.", metadata.getStreamSegmentMetadata(SEGMENT_ID).isSealed());
AssertExtensions.assertThrows("Unexpected behavior for preProcess(Seal) when Segment is sealed (in metadata).", () -> txn2.preProcessOperation(createSeal()), ex -> ex instanceof StreamSegmentSealedException);
}
Aggregations