use of io.pravega.common.ObjectClosedException in project pravega by pravega.
the class DurableLog method doStop.
@Override
protected void doStop() {
long traceId = LoggerHelpers.traceEnterWithContext(log, traceObjectId, "doStop");
log.info("{}: Stopping.", this.traceObjectId);
Services.stopAsync(this.operationProcessor, this.executor).whenCompleteAsync((r, ex) -> {
cancelTailReads();
this.durableDataLog.close();
Throwable cause = this.stopException.get();
if (cause == null && this.operationProcessor.state() == State.FAILED) {
cause = this.operationProcessor.failureCause();
}
// Terminate the delayed start future now, if still active.
this.delayedStart.completeExceptionally(cause == null ? new ObjectClosedException(this) : cause);
if (cause == null) {
// Normal shutdown.
notifyStopped();
} else {
// Shutdown caused by some failure.
notifyFailed(cause);
}
log.info("{}: Stopped.", this.traceObjectId);
LoggerHelpers.traceLeave(log, traceObjectId, "doStop", traceId);
}, this.executor).exceptionally(ex -> {
notifyFailed(ex);
return null;
});
}
use of io.pravega.common.ObjectClosedException in project pravega by pravega.
the class MemoryStateUpdater method addToReadIndex.
/**
* Registers the given operation in the ReadIndex.
*
* @param operation The operation to register.
*/
private void addToReadIndex(StorageOperation operation) {
try {
if (operation instanceof StreamSegmentAppendOperation) {
// Record a StreamSegmentAppendOperation. Just in case, we also support this type of operation, but we need to
// log a warning indicating so. This means we do not optimize memory properly, and we end up storing data
// in two different places.
StreamSegmentAppendOperation appendOperation = (StreamSegmentAppendOperation) operation;
this.readIndex.append(appendOperation.getStreamSegmentId(), appendOperation.getStreamSegmentOffset(), appendOperation.getData());
} else if (operation instanceof MergeTransactionOperation) {
// Record a MergeTransactionOperation. We call beginMerge here, and the StorageWriter will call completeMerge.
MergeTransactionOperation mergeOperation = (MergeTransactionOperation) operation;
this.readIndex.beginMerge(mergeOperation.getStreamSegmentId(), mergeOperation.getStreamSegmentOffset(), mergeOperation.getTransactionSegmentId());
} else {
assert !(operation instanceof CachedStreamSegmentAppendOperation) : "attempted to add a CachedStreamSegmentAppendOperation to the ReadIndex";
}
} catch (ObjectClosedException | StreamSegmentNotExistsException ex) {
// The Segment is in the process of being deleted. We usually end up in here because a concurrent delete
// request has updated the metadata while we were executing.
log.warn("Not adding operation '{}' to ReadIndex because it refers to a deleted StreamSegment.", operation);
}
}
use of io.pravega.common.ObjectClosedException in project pravega by pravega.
the class RedirectedReadResultEntryTests method testRequestContent.
/**
* Tests the ability to retry (and switch base) when a failure occurred in requestContent().
*/
@Test
public void testRequestContent() {
// More than one retry (by design, it will only retry one time; the next time it will simply throw).
FailureReadResultEntry f1 = new FailureReadResultEntry(ReadResultEntryType.Cache, 1, 1, () -> {
throw new ObjectClosedException(this);
});
RedirectedReadResultEntry e1 = new TestRedirectedReadResultEntry(f1, 0, (o, l) -> f1, executorService());
AssertExtensions.assertThrows("requestContent did not throw when attempting to retry more than once.", () -> e1.requestContent(TIMEOUT), ex -> ex instanceof ObjectClosedException);
// Ineligible exception.
FailureReadResultEntry f2 = new FailureReadResultEntry(ReadResultEntryType.Cache, 1, 1, () -> {
throw new IllegalArgumentException();
});
RedirectedReadResultEntry e2 = new TestRedirectedReadResultEntry(f1, 0, (o, l) -> f2, executorService());
AssertExtensions.assertThrows("requestContent did not throw when an ineligible exception got thrown.", () -> e2.requestContent(TIMEOUT), ex -> ex instanceof IllegalArgumentException);
// Given back another Redirect.
RedirectedReadResultEntry e3 = new TestRedirectedReadResultEntry(f1, 0, (o, l) -> e1, executorService());
AssertExtensions.assertThrows("requestContent did not throw when retry yielded another RedirectReadResultEntry.", () -> e3.requestContent(TIMEOUT), ex -> ex instanceof ObjectClosedException);
// Given redirect function fails.
RedirectedReadResultEntry e4 = new TestRedirectedReadResultEntry(f1, 0, (o, l) -> {
throw new IntentionalException();
}, executorService());
AssertExtensions.assertThrows("requestContent did not throw when retry failed.", () -> e4.requestContent(TIMEOUT), ex -> ex instanceof IntentionalException);
// One that works correctly.
AtomicBoolean requestInvoked = new AtomicBoolean();
FailureReadResultEntry f5 = new FailureReadResultEntry(ReadResultEntryType.Cache, 2, 1, () -> requestInvoked.set(true));
f1.setCompletionCallback(i -> {
// Do nothing.
});
RedirectedReadResultEntry e5 = new TestRedirectedReadResultEntry(f1, 1, (o, l) -> f5, executorService());
e5.requestContent(TIMEOUT);
Assert.assertTrue("requestTimeout was not invoked for successful redirect.", requestInvoked.get());
Assert.assertEquals("Unexpected result from getCompletionCallback after successful redirect.", f5.getCompletionCallback(), f1.getCompletionCallback());
Assert.assertEquals("Unexpected result from getRequestedReadLength after successful redirect.", f5.getRequestedReadLength(), e5.getRequestedReadLength());
Assert.assertEquals("Unexpected result from getStreamSegmentOffset after successful redirect.", f5.getStreamSegmentOffset(), e5.getStreamSegmentOffset());
}
use of io.pravega.common.ObjectClosedException in project pravega by pravega.
the class BookKeeperLogTests method testAutoCloseOnBookieFailure.
/**
* Tests the ability to auto-close upon a permanent write failure caused by BookKeeper.
*
* @throws Exception If one got thrown.
*/
@Test
public void testAutoCloseOnBookieFailure() throws Exception {
try (DurableDataLog log = createDurableDataLog()) {
log.initialize(TIMEOUT);
try {
// Suspend a bookie (this will trigger write errors).
stopFirstBookie();
// First write should fail. Either a DataLogNotAvailableException (insufficient bookies) or
// WriteFailureException (general unable to write) should be thrown.
AssertExtensions.assertThrows("First write did not fail with the appropriate exception.", () -> log.append(new ByteArraySegment(getWriteData()), TIMEOUT), ex -> ex instanceof RetriesExhaustedException && (ex.getCause() instanceof DataLogNotAvailableException || isLedgerClosedException(ex.getCause())) || ex instanceof ObjectClosedException || ex instanceof CancellationException);
// Subsequent writes should be rejected since the BookKeeperLog is now closed.
AssertExtensions.assertThrows("Second write did not fail with the appropriate exception.", () -> log.append(new ByteArraySegment(getWriteData()), TIMEOUT), ex -> ex instanceof ObjectClosedException || ex instanceof CancellationException);
} finally {
// Don't forget to resume the bookie.
restartFirstBookie();
}
}
}
use of io.pravega.common.ObjectClosedException in project pravega by pravega.
the class DataFrameBuilderTests method testAppendWithCommitFailure.
/**
* Tests the case when the DataLog fails to commit random frames.
* Commit errors should affect only the LogItems that were part of it. It should cause data to be dropped
* and affected appends failed.
* This should be done both with large and with small LogItems. Large items span multiple frames.
*/
@Test
public void testAppendWithCommitFailure() throws Exception {
// Fail the commit to DurableDataLog after this many writes.
int failAt = 7;
List<TestLogItem> records = DataFrameTestHelpers.generateLogItems(RECORD_COUNT / 2, SMALL_RECORD_MIN_SIZE, SMALL_RECORD_MAX_SIZE, 0);
records.addAll(DataFrameTestHelpers.generateLogItems(RECORD_COUNT / 2, LARGE_RECORD_MIN_SIZE, LARGE_RECORD_MAX_SIZE, records.size()));
@Cleanup TestDurableDataLog dataLog = TestDurableDataLog.create(CONTAINER_ID, FRAME_SIZE, executorService());
dataLog.initialize(TIMEOUT);
val asyncInjector = new ErrorInjector<Exception>(count -> count >= failAt, IntentionalException::new);
dataLog.setAppendErrorInjectors(null, asyncInjector);
AtomicInteger failCount = new AtomicInteger();
List<DataFrameBuilder.CommitArgs> successCommits = Collections.synchronizedList(new ArrayList<>());
// Keep a reference to the builder (once created) so we can inspect its failure cause).
val builderRef = new AtomicReference<DataFrameBuilder>();
val attemptCount = new AtomicInteger();
BiConsumer<Throwable, DataFrameBuilder.CommitArgs> errorCallback = (ex, a) -> {
attemptCount.decrementAndGet();
// Check that we actually did want an exception to happen.
Throwable expectedError = Exceptions.unwrap(asyncInjector.getLastCycleException());
Assert.assertNotNull("An error happened but none was expected: " + ex, expectedError);
Throwable actualError = Exceptions.unwrap(ex);
if (!(ex instanceof ObjectClosedException)) {
// First failure.
Assert.assertEquals("Unexpected error occurred upon commit.", expectedError, actualError);
}
if (builderRef.get().failureCause() != null) {
checkFailureCause(builderRef.get(), ce -> ce instanceof IntentionalException);
}
failCount.incrementAndGet();
};
val args = new DataFrameBuilder.Args(ca -> attemptCount.incrementAndGet(), successCommits::add, errorCallback, executorService());
try (DataFrameBuilder<TestLogItem> b = new DataFrameBuilder<>(dataLog, SERIALIZER, args)) {
builderRef.set(b);
try {
for (val r : records) {
b.append(r);
}
b.close();
} catch (ObjectClosedException ex) {
await(() -> b.failureCause() != null, 20);
// If DataFrameBuilder is closed, then we must have had an exception thrown via the callback before.
Assert.assertNotNull("DataFrameBuilder is closed, yet failure cause is not set yet.", b.failureCause());
checkFailureCause(b, ce -> ce instanceof IntentionalException);
}
}
await(() -> successCommits.size() >= attemptCount.get(), 20);
// Read all committed items.
val reader = new DataFrameReader<TestLogItem>(dataLog, new TestSerializer(), CONTAINER_ID);
val readItems = new ArrayList<TestLogItem>();
DataFrameRecord<TestLogItem> readItem;
while ((readItem = reader.getNext()) != null) {
readItems.add(readItem.getItem());
}
val lastCommitSeqNo = successCommits.stream().mapToLong(DataFrameBuilder.CommitArgs::getLastFullySerializedSequenceNumber).max().orElse(-1);
val expectedItems = records.stream().filter(r -> r.getSequenceNumber() <= lastCommitSeqNo).collect(Collectors.toList());
AssertExtensions.assertListEquals("Items read back do not match expected values.", expectedItems, readItems, TestLogItem::equals);
// Read all entries in the Log and interpret them as DataFrames, then verify the records can be reconstructed.
val frames = dataLog.getAllEntries(ri -> DataFrame.read(ri.getPayload(), ri.getLength(), ri.getAddress()));
// Check the correctness of the commit callback.
AssertExtensions.assertGreaterThan("Not enough Data Frames were generated.", 1, frames.size());
Assert.assertEquals("Unexpected number of frames generated.", successCommits.size(), frames.size());
}
Aggregations