use of io.pravega.segmentstore.server.ContainerOfflineException 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.ContainerOfflineException in project pravega by pravega.
the class StreamSegmentContainerTests method testStartOffline.
/**
* Tests the ability of the StreamSegmentContainer to start in Offline mode (due to an offline DurableLog) and eventually
* become online when the DurableLog becomes too.
*/
@Test
public void testStartOffline() throws Exception {
@Cleanup val context = createContext();
AtomicReference<DurableDataLog> dataLog = new AtomicReference<>();
@Cleanup val dataLogFactory = new TestDurableDataLogFactory(context.dataLogFactory, dataLog::set);
AtomicReference<OperationLog> durableLog = new AtomicReference<>();
val durableLogFactory = new WatchableOperationLogFactory(new DurableLogFactory(DEFAULT_DURABLE_LOG_CONFIG, dataLogFactory, executorService()), durableLog::set);
val containerFactory = new StreamSegmentContainerFactory(DEFAULT_CONFIG, durableLogFactory, context.readIndexFactory, context.attributeIndexFactory, context.writerFactory, context.storageFactory, context.getDefaultExtensions(), executorService());
// Write some data
ArrayList<String> segmentNames = new ArrayList<>();
HashMap<String, Long> lengths = new HashMap<>();
HashMap<String, ByteArrayOutputStream> segmentContents = new HashMap<>();
try (val container = containerFactory.createStreamSegmentContainer(CONTAINER_ID)) {
container.startAsync().awaitRunning();
val creationFutures = new ArrayList<CompletableFuture<Void>>();
for (int i = 0; i < SEGMENT_COUNT; i++) {
String segmentName = getSegmentName(i);
segmentNames.add(segmentName);
creationFutures.add(container.createStreamSegment(segmentName, getSegmentType(segmentName), null, TIMEOUT));
}
// Wait for the segments to be created first.
Futures.allOf(creationFutures).join();
val opFutures = new ArrayList<CompletableFuture<Long>>();
for (int i = 0; i < APPENDS_PER_SEGMENT / 2; i++) {
for (String segmentName : segmentNames) {
RefCountByteArraySegment appendData = getAppendData(segmentName, i);
opFutures.add(container.append(segmentName, appendData, null, TIMEOUT));
lengths.put(segmentName, lengths.getOrDefault(segmentName, 0L) + appendData.getLength());
recordAppend(segmentName, appendData, segmentContents, null);
}
}
Futures.allOf(opFutures).join();
// Disable the DurableDataLog.
dataLog.get().disable();
}
// Start in "Offline" mode, verify operations cannot execute and then shut down - make sure we can shut down an offline container.
try (val container = containerFactory.createStreamSegmentContainer(CONTAINER_ID)) {
container.startAsync().awaitRunning();
Assert.assertTrue("Expecting Segment Container to be offline.", container.isOffline());
AssertExtensions.assertSuppliedFutureThrows("append() worked in offline mode.", () -> container.append("foo", new ByteArraySegment(new byte[1]), null, TIMEOUT), ex -> ex instanceof ContainerOfflineException);
AssertExtensions.assertSuppliedFutureThrows("getStreamSegmentInfo() worked in offline mode.", () -> container.getStreamSegmentInfo("foo", TIMEOUT), ex -> ex instanceof ContainerOfflineException);
AssertExtensions.assertSuppliedFutureThrows("read() worked in offline mode.", () -> container.read("foo", 0, 1, TIMEOUT), ex -> ex instanceof ContainerOfflineException);
container.stopAsync().awaitTerminated();
}
// Start in "Offline" mode and verify we can resume a normal startup.
try (val container = containerFactory.createStreamSegmentContainer(CONTAINER_ID)) {
container.startAsync().awaitRunning();
Assert.assertTrue("Expecting Segment Container to be offline.", container.isOffline());
dataLog.get().enable();
// Wait for the DurableLog to become online.
durableLog.get().awaitOnline().get(DEFAULT_DURABLE_LOG_CONFIG.getStartRetryDelay().toMillis() * 100, TimeUnit.MILLISECONDS);
// Verify we can execute regular operations now.
ArrayList<CompletableFuture<Void>> opFutures = new ArrayList<>();
for (int i = 0; i < APPENDS_PER_SEGMENT / 2; i++) {
for (String segmentName : segmentNames) {
RefCountByteArraySegment appendData = getAppendData(segmentName, i);
opFutures.add(Futures.toVoid(container.append(segmentName, appendData, null, TIMEOUT)));
lengths.put(segmentName, lengths.getOrDefault(segmentName, 0L) + appendData.getLength());
recordAppend(segmentName, appendData, segmentContents, null);
}
}
Futures.allOf(opFutures).join();
// Verify all operations arrived in Storage.
ArrayList<CompletableFuture<Void>> segmentsCompletion = new ArrayList<>();
for (String segmentName : segmentNames) {
SegmentProperties sp = container.getStreamSegmentInfo(segmentName, TIMEOUT).join();
segmentsCompletion.add(waitForSegmentInStorage(sp, context));
}
Futures.allOf(segmentsCompletion).join();
container.stopAsync().awaitTerminated();
}
}
Aggregations