use of com.github.ambry.commons.ByteBufferAsyncWritableChannel in project ambry by linkedin.
the class GetBlobOperationTest method testMissingDataChunkException.
/**
* Test when data chunks are missing, the getBlob should return RestServiceException.
* @throws Exception
*/
@Test
public void testMissingDataChunkException() throws Exception {
// Make sure we have two data chunks
blobSize = maxChunkSize + maxChunkSize / 2;
doPut();
// Now remove the all the data chunks, first collect data chunk ids and remove blob id
Set<String> dataChunkIds = new HashSet<>();
mockServerLayout.getMockServers().forEach(server -> dataChunkIds.addAll(server.getBlobs().keySet()));
dataChunkIds.remove(blobIdStr);
Assert.assertEquals(2, dataChunkIds.size());
// We can't just delete the data chunks by calling router.deleteBlob, we have to remove them from the mock servers.
mockServerLayout.getMockServers().forEach(server -> dataChunkIds.forEach(chunkId -> server.getBlobs().remove(chunkId)));
GetBlobResult result = router.getBlob(blobIdStr, new GetBlobOptionsBuilder().build()).get();
Future<Long> future = result.getBlobDataChannel().readInto(new ByteBufferAsyncWritableChannel(), null);
try {
future.get();
Assert.fail("Should fail with exception");
} catch (Exception e) {
Throwable cause = e.getCause();
Assert.assertTrue(cause instanceof RestServiceException);
Assert.assertEquals(RestServiceErrorCode.InternalServerError, ((RestServiceException) cause).getErrorCode());
Assert.assertTrue(((RestServiceException) cause).shouldIncludeExceptionMessageInResponse());
}
}
use of com.github.ambry.commons.ByteBufferAsyncWritableChannel in project ambry by linkedin.
the class GetBlobOperationTest method testEarlyReadableStreamChannelClose.
/**
* Test that the operation is completed and an exception with the error code {@link RouterErrorCode#ChannelClosed} is
* set when the {@link ReadableStreamChannel} is closed before all chunks are read for a specific blob size and
* number of chunks to read.
* @param numChunksInBlob the number of chunks in the blob to put/get.
* @param numChunksToRead the number of chunks to read from the {@link AsyncWritableChannel} before closing the
* {@link ReadableStreamChannel}.
* @throws Exception
*/
private void testEarlyReadableStreamChannelClose(int numChunksInBlob, final int numChunksToRead) throws Exception {
final AtomicReference<Exception> callbackException = new AtomicReference<>();
final AtomicReference<Future<Long>> readIntoFuture = new AtomicReference<>();
final CountDownLatch readCompleteLatch = new CountDownLatch(1);
Callback<GetBlobResultInternal> callback = new Callback<GetBlobResultInternal>() {
@Override
public void onCompletion(final GetBlobResultInternal result, Exception exception) {
if (exception != null) {
callbackException.set(exception);
readCompleteLatch.countDown();
} else {
final ByteBufferAsyncWritableChannel writableChannel = new ByteBufferAsyncWritableChannel();
readIntoFuture.set(result.getBlobResult.getBlobDataChannel().readInto(writableChannel, null));
Utils.newThread(new Runnable() {
@Override
public void run() {
try {
int chunksLeftToRead = numChunksToRead;
while (chunksLeftToRead > 0) {
writableChannel.getNextChunk();
writableChannel.resolveOldestChunk(null);
chunksLeftToRead--;
}
result.getBlobResult.getBlobDataChannel().close();
while (writableChannel.getNextChunk(100) != null) {
writableChannel.resolveOldestChunk(null);
}
} catch (Exception e) {
callbackException.set(e);
} finally {
readCompleteLatch.countDown();
}
}
}, false).start();
}
}
};
blobSize = numChunksInBlob * maxChunkSize;
doPut();
GetBlobOperation op = createOperationAndComplete(callback);
Assert.assertTrue("Timeout waiting for read to complete", readCompleteLatch.await(2, TimeUnit.SECONDS));
if (callbackException.get() != null) {
throw callbackException.get();
}
try {
readIntoFuture.get().get();
Assert.fail("Expected ExecutionException");
} catch (ExecutionException e) {
Assert.assertTrue("Unexpected type for exception: " + e.getCause(), e.getCause() instanceof RouterException);
Assert.assertEquals("Unexpected RouterErrorCode", RouterErrorCode.ChannelClosed, ((RouterException) e.getCause()).getErrorCode());
}
Exception operationException = op.getOperationException();
Assert.assertTrue("Unexpected type for exception: " + operationException, operationException instanceof RouterException);
Assert.assertEquals("Unexpected RouterErrorCode", RouterErrorCode.ChannelClosed, ((RouterException) operationException).getErrorCode());
}
use of com.github.ambry.commons.ByteBufferAsyncWritableChannel in project ambry by linkedin.
the class GetBlobOperationTest method testDataChunkError.
/**
* Test that an operation is completed with a specified {@link RouterErrorCode} when all gets on data chunks
* in a multi-part blob return a specified {@link ServerErrorCode}
* @param serverErrorCode The error code to be returned when fetching data chunks.
* @param expectedErrorCode The operation's expected error code.
* @throws Exception
*/
private void testDataChunkError(ServerErrorCode serverErrorCode, final RouterErrorCode expectedErrorCode) throws Exception {
blobSize = maxChunkSize * 2 + 1;
doPut();
final CountDownLatch readCompleteLatch = new CountDownLatch(1);
final AtomicReference<Exception> readCompleteException = new AtomicReference<>(null);
final ByteBufferAsyncWritableChannel asyncWritableChannel = new ByteBufferAsyncWritableChannel();
mockServerLayout.getMockServers().forEach(mockServer -> mockServer.setGetErrorOnDataBlobOnly(true));
RouterTestHelpers.testWithErrorCodes(Collections.singletonMap(serverErrorCode, 9), mockServerLayout, expectedErrorCode, new ErrorCodeChecker() {
@Override
public void testAndAssert(RouterErrorCode expectedError) throws Exception {
Callback<GetBlobResultInternal> callback = new Callback<GetBlobResultInternal>() {
@Override
public void onCompletion(final GetBlobResultInternal result, final Exception exception) {
if (exception != null) {
asyncWritableChannel.close();
readCompleteLatch.countDown();
} else {
Utils.newThread(new Runnable() {
@Override
public void run() {
try {
result.getBlobResult.getBlobDataChannel().readInto(asyncWritableChannel, new Callback<Long>() {
@Override
public void onCompletion(Long result, Exception exception) {
asyncWritableChannel.close();
}
});
asyncWritableChannel.getNextChunk();
} catch (Exception e) {
readCompleteException.set(e);
} finally {
readCompleteLatch.countDown();
}
}
}, false).start();
}
}
};
GetBlobOperation op = createOperationAndComplete(callback);
Assert.assertTrue(readCompleteLatch.await(2, TimeUnit.SECONDS));
Assert.assertTrue("Operation should be complete at this time", op.isOperationComplete());
if (readCompleteException.get() != null) {
throw readCompleteException.get();
}
Assert.assertFalse("AsyncWriteableChannel should have been closed.", asyncWritableChannel.isOpen());
assertFailureAndCheckErrorCode(op, expectedError);
}
});
}
use of com.github.ambry.commons.ByteBufferAsyncWritableChannel in project ambry by linkedin.
the class RouterTestHelpers method compareContent.
/**
* Compare and assert that the content in the given {@link ReadableStreamChannel} is exactly the same as
* the original content argument.
* @param originalContent the content array to check against.
* @param range if non-null, apply this range to {@code originalContent} before comparison.
* @param readableStreamChannel the {@link ReadableStreamChannel} that is the candidate for comparison.
*/
static void compareContent(byte[] originalContent, ByteRange range, ReadableStreamChannel readableStreamChannel) throws ExecutionException, InterruptedException {
ByteBuffer putContentBuf = ByteBuffer.wrap(originalContent);
// If a range is set, compare the result against the specified byte range.
if (range != null) {
ByteRange resolvedRange = range.toResolvedByteRange(originalContent.length);
putContentBuf = ByteBuffer.wrap(originalContent, (int) resolvedRange.getStartOffset(), (int) resolvedRange.getRangeSize());
}
ByteBufferAsyncWritableChannel getChannel = new ByteBufferAsyncWritableChannel();
Future<Long> readIntoFuture = readableStreamChannel.readInto(getChannel, null);
final int bytesToRead = putContentBuf.remaining();
int readBytes = 0;
do {
ByteBuffer buf = getChannel.getNextChunk();
int bufLength = buf.remaining();
assertTrue("total content read should not be greater than length of put content", readBytes + bufLength <= bytesToRead);
while (buf.hasRemaining()) {
assertEquals("Get and Put blob content should match", putContentBuf.get(), buf.get());
readBytes++;
}
getChannel.resolveOldestChunk(null);
} while (readBytes < bytesToRead);
assertEquals("the returned length in the future should be the length of data written", (long) readBytes, (long) readIntoFuture.get());
assertNull("There should be no more data in the channel", getChannel.getNextChunk(0));
}
use of com.github.ambry.commons.ByteBufferAsyncWritableChannel in project ambry by linkedin.
the class RouterServerTestFramework method checkBlob.
/**
* Check that the blob read from {@code channel} matches {@code opChain.data}.
* @param channel the {@link ReadableStreamChannel} to check
* @param opChain the {@link OperationChain} structure to compare against
* @param operationName a name for the operation being checked
*/
private static void checkBlob(ReadableStreamChannel channel, OperationChain opChain, String operationName) {
Assert.assertNotNull("Null channel for operation: " + operationName, channel);
try {
ByteBufferAsyncWritableChannel getChannel = new ByteBufferAsyncWritableChannel();
Future<Long> readIntoFuture = channel.readInto(getChannel, null);
int readBytes = 0;
do {
ByteBuffer buf = getChannel.getNextChunk();
int bufLength = buf.remaining();
Assert.assertTrue("total content read should not be greater than length of put content, operation: " + operationName, readBytes + bufLength <= opChain.data.length);
while (buf.hasRemaining()) {
Assert.assertEquals("Get and Put blob content should match, operation: " + operationName, opChain.data[readBytes++], buf.get());
}
getChannel.resolveOldestChunk(null);
} while (readBytes < opChain.data.length);
Assert.assertEquals("the returned length in the future should be the length of data written, operation: " + operationName, (long) readBytes, (long) readIntoFuture.get(AWAIT_TIMEOUT, TimeUnit.SECONDS));
Assert.assertNull("There should be no more data in the channel, operation: " + operationName, getChannel.getNextChunk(0));
} catch (Exception e) {
Assert.fail("Exception while reading from getChannel from operation: " + operationName);
}
}
Aggregations