use of io.pravega.test.common.IntentionalException 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.test.common.IntentionalException in project pravega by pravega.
the class StreamSegmentContainerRegistryTests method testContainerFailureWhileRunning.
/**
* Tests the ability to detect a container failure and unregister the container in case the container fails while running.
*/
@Test
public void testContainerFailureWhileRunning() throws Exception {
final int containerId = 123;
TestContainerFactory factory = new TestContainerFactory();
@Cleanup StreamSegmentContainerRegistry registry = new StreamSegmentContainerRegistry(factory, executorService());
ContainerHandle handle = registry.startContainer(containerId, TIMEOUT).join();
// Register a Listener for the Container.Stop event. Make this a Future since these callbacks are invoked async
// so they may finish executing after stop() finished.
CompletableFuture<Integer> stopListenerCallback = new CompletableFuture<>();
handle.setContainerStoppedListener(stopListenerCallback::complete);
TestContainer container = (TestContainer) registry.getContainer(handle.getContainerId());
// Fail the container and wait for it to properly terminate.
container.fail(new IntentionalException());
ServiceListeners.awaitShutdown(container, false);
Assert.assertEquals("Unexpected value passed to Handle.stopListenerCallback or callback was not invoked.", containerId, (int) stopListenerCallback.join());
AssertExtensions.assertThrows("Container is still registered after failure.", () -> registry.getContainer(containerId), ex -> ex instanceof ContainerNotFoundException);
}
use of io.pravega.test.common.IntentionalException in project pravega by pravega.
the class WriteQueueTests method testRemoveFinishedWrites.
/**
* Tests the removeFinishedWrites() method.
*/
@Test
public void testRemoveFinishedWrites() {
// Just over 1ms.
final int timeIncrement = 1234 * 1000;
AtomicLong time = new AtomicLong();
val q = new WriteQueue(time::get);
val writes = new ArrayDeque<Write>();
for (int i = 0; i < ITEM_COUNT; i++) {
time.addAndGet(timeIncrement);
val w = new Write(new ByteArraySegment(new byte[i]), new TestWriteLedger(i), new CompletableFuture<>());
if (i % 2 == 0) {
// Complete 1 out of two writes.
w.setEntryId(i);
w.complete();
}
q.add(w);
writes.addLast(w);
}
while (!writes.isEmpty()) {
val write = writes.pollFirst();
if (!write.isDone()) {
val result1 = q.removeFinishedWrites();
AssertExtensions.assertContainsSameElements("Unexpected value from removeFinishedWrites when there were writes left in the queue.", EnumSet.of(WriteQueue.CleanupStatus.QueueNotEmpty), result1);
val stats1 = q.getStatistics();
Assert.assertEquals("Unexpected size after removeFinishedWrites with no effect.", writes.size() + 1, stats1.getSize());
// Complete this write.
write.setEntryId(time.get());
write.complete();
}
// Estimate the Expected elapsed time based on the removals.
long expectedElapsed = write.getQueueAddedTimestamp();
int removed = 1;
while (!writes.isEmpty() && writes.peekFirst().isDone()) {
expectedElapsed += writes.pollFirst().getQueueAddedTimestamp();
removed++;
}
expectedElapsed = (time.get() * removed - expectedElapsed) / AbstractTimer.NANOS_TO_MILLIS / removed;
val result2 = q.removeFinishedWrites();
val expectedResult = EnumSet.of(writes.isEmpty() ? WriteQueue.CleanupStatus.QueueEmpty : WriteQueue.CleanupStatus.QueueNotEmpty);
AssertExtensions.assertContainsSameElements("Unexpected result from removeFinishedWrites.", expectedResult, result2);
val stats2 = q.getStatistics();
Assert.assertEquals("Unexpected size after removeFinishedWrites.", writes.size(), stats2.getSize());
Assert.assertEquals("Unexpected getExpectedProcessingTimeMillis after clear.", expectedElapsed, stats2.getExpectedProcessingTimeMillis());
}
// Verify that it does report failed writes when encountered.
val w3 = new Write(new ByteArraySegment(new byte[1]), new TestWriteLedger(0), new CompletableFuture<>());
q.add(w3);
w3.fail(new IntentionalException(), true);
val result3 = q.removeFinishedWrites();
AssertExtensions.assertContainsSameElements("Unexpected value from removeFinishedWrites when there were failed writes.", EnumSet.of(WriteQueue.CleanupStatus.QueueEmpty, WriteQueue.CleanupStatus.WriteFailed), result3);
}
use of io.pravega.test.common.IntentionalException 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());
}
use of io.pravega.test.common.IntentionalException in project pravega by pravega.
the class DataFrameOutputStreamTests method testCommitFailure.
/**
* Tests the behavior of startNewRecord(), write(byte) and write(byte[]) when the commit callback throws an exception.
*/
@Test
public void testCommitFailure() throws Exception {
int maxFrameSize = 50;
// Callback for when a frame is written. If we need to throw an exception, do it; otherwise just remember the frame.
AtomicReference<DataFrame> writtenFrame = new AtomicReference<>();
AtomicBoolean throwException = new AtomicBoolean();
Consumer<DataFrame> callback = df -> {
if (throwException.get()) {
throw new IntentionalException();
}
writtenFrame.set(df);
};
// Test #1: write(byte)
AtomicInteger usableSpace = new AtomicInteger();
ByteArrayOutputStream writtenData1 = new ByteArrayOutputStream();
try (DataFrameOutputStream s = new DataFrameOutputStream(maxFrameSize, callback)) {
// 1. Call write(byte) until it fails. Check that the correct exception is thrown.
s.startNewRecord();
throwException.set(true);
AssertExtensions.assertThrows("write() did not throw when the commit callback threw an exception.", () -> {
for (int i = 0; i < maxFrameSize; i++) {
s.write((byte) usableSpace.get());
writtenData1.write((byte) usableSpace.get());
usableSpace.incrementAndGet();
}
}, ex -> ex instanceof IntentionalException);
// 2. Call write(byte) again and verify it fails. But this should fail because the DataFrame is sealed
// (it was sealed prior to the current commit attempt).
AssertExtensions.assertThrows("write() did not throw when the frame was sealed post-commit failure.", () -> s.write((byte) 1), ex -> ex instanceof IllegalStateException);
// 3. Allow the commit to succeed. Verify a frame has been committed with the correct content.
throwException.set(false);
s.flush();
Assert.assertNotNull("No frame has been created when a frame was filled.", writtenFrame.get());
ArrayList<byte[]> records = new ArrayList<>();
records.add(writtenData1.toByteArray());
DataFrameTestHelpers.checkReadRecords(readFrame(writtenFrame.get()), records, ByteArraySegment::new);
}
// Test #2: startNewRecord()
ByteArrayOutputStream writtenData2 = new ByteArrayOutputStream();
writtenFrame.set(null);
try (DataFrameOutputStream s = new DataFrameOutputStream(maxFrameSize, callback)) {
// 1. Call write(byte) until we fill up the frame
throwException.set(false);
s.startNewRecord();
for (int i = 0; i < usableSpace.get(); i++) {
s.write((byte) i);
writtenData2.write((byte) i);
}
// 2. Call startNewRecord(). This should fail because it will try to commit the frame.
throwException.set(true);
AssertExtensions.assertThrows("startNewRecord() did not throw when the commit callback threw an exception.", s::startNewRecord, ex -> ex instanceof IntentionalException);
// 3. Allow the commit to succeed. Verify a frame has been committed with the correct content.
throwException.set(false);
s.flush();
Assert.assertNotNull("No frame has been created when a frame was filled.", writtenFrame.get());
ArrayList<byte[]> records = new ArrayList<>();
records.add(writtenData2.toByteArray());
DataFrameTestHelpers.checkReadRecords(readFrame(writtenFrame.get()), records, ByteArraySegment::new);
}
// Test #3: write(byte[])
writtenFrame.set(null);
try (DataFrameOutputStream s = new DataFrameOutputStream(maxFrameSize, callback)) {
// 1. Call write(byte) until we fill up the frame
throwException.set(false);
s.startNewRecord();
for (int i = 0; i < usableSpace.get(); i++) {
// writtenData2 already contains this.
s.write((byte) i);
}
// 2. Call write(byte[]). This should fail because it will try to commit the frame.
throwException.set(true);
AssertExtensions.assertThrows("write(byte[]) did not throw when the commit callback threw an exception.", () -> s.write(new byte[10]), ex -> ex instanceof IntentionalException);
// 3. Allow the commit to succeed. Verify a frame has been committed with the correct content.
throwException.set(false);
s.flush();
Assert.assertNotNull("No frame has been created when a frame was filled.", writtenFrame.get());
ArrayList<byte[]> records = new ArrayList<>();
records.add(writtenData2.toByteArray());
DataFrameTestHelpers.checkReadRecords(readFrame(writtenFrame.get()), records, ByteArraySegment::new);
}
}
Aggregations