use of com.github.ambry.messageformat.BlobProperties in project ambry by linkedin.
the class PutOperationTest method testEmptyByteBufInReadableStreamChannel.
/**
* Test the case when there is an empty bytebuf in the end of the {@link ReadableByteChannel}, make sure that
* setting operation completed to true wouldn't end up with NPE.
* @throws Exception
*/
@Test
public void testEmptyByteBufInReadableStreamChannel() throws Exception {
BlobProperties blobProperties = new BlobProperties(-1, "serviceId", "memberId", "contentType", false, Utils.Infinite_Time, Utils.getRandomShort(TestUtils.RANDOM), Utils.getRandomShort(TestUtils.RANDOM), false, null, null, null);
byte[] userMetadata = new byte[10];
byte[] content = new byte[chunkSize];
random.nextBytes(content);
ReadableStreamChannel channel = new ByteBufferReadableStreamChannel(ByteBuffer.wrap(content), true);
FutureResult<String> future = new FutureResult<>();
MockNetworkClient mockNetworkClient = new MockNetworkClient();
PutOperation op = PutOperation.forUpload(routerConfig, routerMetrics, mockClusterMap, new LoggingNotificationSystem(), new InMemAccountService(true, false), userMetadata, channel, PutBlobOptions.DEFAULT, future, null, new RouterCallback(mockNetworkClient, new ArrayList<>()), null, null, null, null, time, blobProperties, MockClusterMap.DEFAULT_PARTITION_CLASS, quotaChargeCallback);
op.startOperation();
// Calling fillChunks would fetch the buffer chunk and the empty chunk from the channel
op.fillChunks();
// Now fail the operation
op.setOperationCompleted();
// Calling fillChunks again, this would force fillChunk to release the empty chunk
op.fillChunks();
// Now call fillChunk again
op.fillChunks();
Assert.assertNull(op.getOperationException());
}
use of com.github.ambry.messageformat.BlobProperties in project ambry by linkedin.
the class PutOperationTest method testSetOperationExceptionAndComplete.
/**
* Test the Errors {@link RouterErrorCode} received by Put Operation. The operation exception is set
* based on the priority of these errors.
* @throws Exception
*/
@Test
public void testSetOperationExceptionAndComplete() throws Exception {
int numChunks = routerConfig.routerMaxInMemPutChunks + 1;
BlobProperties blobProperties = new BlobProperties(-1, "serviceId", "memberId", "contentType", false, Utils.Infinite_Time, Utils.getRandomShort(TestUtils.RANDOM), Utils.getRandomShort(TestUtils.RANDOM), false, null, null, null);
byte[] userMetadata = new byte[10];
byte[] content = new byte[chunkSize * numChunks];
random.nextBytes(content);
ReadableStreamChannel channel = new ByteBufferReadableStreamChannel(ByteBuffer.wrap(content));
FutureResult<String> future = new FutureResult<>();
MockNetworkClient mockNetworkClient = new MockNetworkClient();
PutOperation op = PutOperation.forUpload(routerConfig, routerMetrics, mockClusterMap, new LoggingNotificationSystem(), new InMemAccountService(true, false), userMetadata, channel, PutBlobOptions.DEFAULT, future, null, new RouterCallback(mockNetworkClient, new ArrayList<>()), null, null, null, null, time, blobProperties, MockClusterMap.DEFAULT_PARTITION_CLASS, quotaChargeCallback);
RouterErrorCode[] routerErrorCodes = new RouterErrorCode[5];
routerErrorCodes[0] = RouterErrorCode.OperationTimedOut;
routerErrorCodes[1] = RouterErrorCode.UnexpectedInternalError;
routerErrorCodes[2] = RouterErrorCode.AmbryUnavailable;
routerErrorCodes[3] = RouterErrorCode.InsufficientCapacity;
routerErrorCodes[4] = RouterErrorCode.InvalidBlobId;
for (int i = 0; i < routerErrorCodes.length; ++i) {
op.setOperationExceptionAndComplete(new RouterException("RouterError", routerErrorCodes[i]));
Assert.assertEquals(((RouterException) op.getOperationException()).getErrorCode(), routerErrorCodes[i]);
}
for (int i = routerErrorCodes.length - 1; i >= 0; --i) {
op.setOperationExceptionAndComplete(new RouterException("RouterError", routerErrorCodes[i]));
Assert.assertEquals(((RouterException) op.getOperationException()).getErrorCode(), routerErrorCodes[routerErrorCodes.length - 1]);
}
Exception nonRouterException = new Exception();
op.setOperationExceptionAndComplete(nonRouterException);
Assert.assertEquals(nonRouterException, op.getOperationException());
// test edge case where current operationException is non RouterException
op.setOperationExceptionAndComplete(new RouterException("RouterError", RouterErrorCode.InsufficientCapacity));
Assert.assertEquals(((RouterException) op.getOperationException()).getErrorCode(), RouterErrorCode.InsufficientCapacity);
}
use of com.github.ambry.messageformat.BlobProperties in project ambry by linkedin.
the class GetManagerTest method setOperationParams.
/**
* Set operation parameters for the blob that will be put and got.
* @param blobSize the blob size for the blob that will be put and got.
* @param options the options for the get request
*/
private void setOperationParams(int blobSize, GetBlobOptions options) {
this.blobSize = blobSize;
putBlobProperties = new BlobProperties(-1, "serviceId", "memberId", "contentType", false, Utils.Infinite_Time, Utils.getRandomShort(TestUtils.RANDOM), Utils.getRandomShort(TestUtils.RANDOM), testEncryption, null, null, null);
putUserMetadata = new byte[10];
random.nextBytes(putUserMetadata);
putContent = new byte[blobSize];
random.nextBytes(putContent);
putChannel = new ByteBufferReadableStreamChannel(ByteBuffer.wrap(putContent));
this.options = options;
}
use of com.github.ambry.messageformat.BlobProperties in project ambry by linkedin.
the class CloudOperationTest method testGetBlobFailoverToAzureAndCheckData.
/**
* Disk backed DC all returns failure but cloud backed DC returns the Blob information successfully.
* Check the meta data and user data are correctly returned by the cloud colo.
*/
@Test
public void testGetBlobFailoverToAzureAndCheckData() throws Exception {
// a blob size that is greater than the maxChunkSize and is not a multiple of it. Will result in a composite blob.
int blobSize = maxChunkSize * random.nextInt(10) + random.nextInt(maxChunkSize - 1) + 1;
BlobProperties blobProperties = new BlobProperties(blobSize, "serviceId", "memberId", "contentType", false, Utils.Infinite_Time, Utils.getRandomShort(random), Utils.getRandomShort(random), false, null, null, null);
byte[] userMetadata = new byte[10];
random.nextBytes(userMetadata);
byte[] putContent = new byte[blobSize];
random.nextBytes(putContent);
ByteBuf putContentBuf = PooledByteBufAllocator.DEFAULT.heapBuffer(blobSize);
putContentBuf.writeBytes(putContent);
BlobId blobId = doDirectPut(blobProperties, userMetadata, putContentBuf.retainedDuplicate());
putContentBuf.release();
// Confirm we can get the blob from the local dc.
GetBlobOptionsInternal options = new GetBlobOptionsInternal(new GetBlobOptionsBuilder().build(), false, routerMetrics.ageAtGet);
getBlobAndAssertSuccess(blobId, (short) 0, blobSize, blobProperties, userMetadata, putContent, options);
// Local DC will fail with different errors but cloud will return the blob data.
ArrayList<MockServer> mockServersArray = new ArrayList<>(mockServers);
ArrayList<ServerErrorCode> serverErrors = new ArrayList<>(Arrays.asList(ServerErrorCode.values()));
// set the disk backed server status to various server level or partition level errors (not Blob_Deleted or Blob_Expired - as they are final)
serverErrors.remove(ServerErrorCode.Blob_Deleted);
serverErrors.remove(ServerErrorCode.Blob_Expired);
serverErrors.remove(ServerErrorCode.No_Error);
serverErrors.remove(ServerErrorCode.Blob_Authorization_Failure);
for (MockServer mockServer : mockServersArray) {
ServerErrorCode code = serverErrors.get(random.nextInt(serverErrors.size()));
mockServer.setServerErrorForAllRequests(code);
}
// cloud DC will return the data
getBlobAndAssertSuccess(blobId, (short) 0, blobSize, blobProperties, userMetadata, putContent, options);
}
use of com.github.ambry.messageformat.BlobProperties in project ambry by linkedin.
the class ServerTestUtil method undeleteCornerCasesTest.
static void undeleteCornerCasesTest(MockCluster cluster, PortType portType, SSLConfig clientSSLConfig1, SSLConfig clientSSLConfig2, SSLConfig clientSSLConfig3, SSLSocketFactory clientSSLSocketFactory1, SSLSocketFactory clientSSLSocketFactory2, SSLSocketFactory clientSSLSocketFactory3, MockNotificationSystem notificationSystem, Properties routerProps, boolean testEncryption) {
MockClusterMap clusterMap = cluster.getClusterMap();
byte[] userMetadata = new byte[1000];
byte[] data = new byte[31870];
byte[] encryptionKey = new byte[100];
short accountId = Utils.getRandomShort(TestUtils.RANDOM);
short containerId = Utils.getRandomShort(TestUtils.RANDOM);
BlobProperties properties = new BlobProperties(31870, "serviceid1", accountId, containerId, testEncryption, cluster.time.milliseconds());
TestUtils.RANDOM.nextBytes(userMetadata);
TestUtils.RANDOM.nextBytes(data);
if (testEncryption) {
TestUtils.RANDOM.nextBytes(encryptionKey);
}
short blobIdVersion = CommonTestUtils.getCurrentBlobIdVersion();
Map<String, List<DataNodeId>> dataNodesPerDC = clusterMap.getDataNodes().stream().collect(Collectors.groupingBy(DataNodeId::getDatacenterName));
Map<String, Pair<SSLConfig, SSLSocketFactory>> sslSettingPerDC = new HashMap<>();
sslSettingPerDC.put("DC1", new Pair<>(clientSSLConfig1, clientSSLSocketFactory1));
sslSettingPerDC.put("DC2", new Pair<>(clientSSLConfig2, clientSSLSocketFactory2));
sslSettingPerDC.put("DC3", new Pair<>(clientSSLConfig3, clientSSLSocketFactory3));
List<PartitionId> partitionIds = clusterMap.getWritablePartitionIds(MockClusterMap.DEFAULT_PARTITION_CLASS);
DataNodeId dataNodeId = dataNodesPerDC.get("DC1").get(0);
Router router = null;
try {
Properties routerProperties = getRouterProps("DC1");
routerProperties.putAll(routerProps);
VerifiableProperties routerVerifiableProps = new VerifiableProperties(routerProperties);
AccountService accountService = new InMemAccountService(false, true);
router = new NonBlockingRouterFactory(routerVerifiableProps, clusterMap, new MockNotificationSystem(clusterMap), getSSLFactoryIfRequired(routerVerifiableProps), accountService).getRouter();
// channels to all datanodes
List<ConnectedChannel> channels = new ArrayList<>();
for (Map.Entry<String, List<DataNodeId>> entry : dataNodesPerDC.entrySet()) {
Pair<SSLConfig, SSLSocketFactory> pair = sslSettingPerDC.get(entry.getKey());
for (DataNodeId node : entry.getValue()) {
ConnectedChannel connectedChannel = getBlockingChannelBasedOnPortType(portType, node, pair.getSecond(), pair.getFirst());
connectedChannel.connect();
channels.add(connectedChannel);
}
}
// ////////////////////////////////////////////////////
// Corner case 1: When only one datacenter has delete
// ////////////////////////////////////////////////////
BlobId blobId1 = new BlobId(blobIdVersion, BlobId.BlobIdType.NATIVE, clusterMap.getLocalDatacenterId(), properties.getAccountId(), properties.getContainerId(), partitionIds.get(0), false, BlobId.BlobDataType.DATACHUNK);
ConnectedChannel channel = getBlockingChannelBasedOnPortType(portType, dataNodeId, clientSSLSocketFactory1, clientSSLConfig1);
channel.connect();
PutRequest putRequest = new PutRequest(1, "client1", blobId1, properties, ByteBuffer.wrap(userMetadata), Unpooled.wrappedBuffer(data), properties.getBlobSize(), BlobType.DataBlob, testEncryption ? ByteBuffer.wrap(encryptionKey) : null);
DataInputStream putResponseStream = channel.sendAndReceive(putRequest).getInputStream();
PutResponse response = PutResponse.readFrom(putResponseStream);
releaseNettyBufUnderneathStream(putResponseStream);
assertEquals(ServerErrorCode.No_Error, response.getError());
notificationSystem.awaitBlobCreations(blobId1.toString());
// Now stop the replications this partition.
PartitionId partitionId = blobId1.getPartition();
controlReplicationForPartition(channels, partitionId, false);
// Now send the delete to two data nodes in the same DC
List<DataNodeId> toBeDeleteDataNodes = dataNodesPerDC.values().stream().findFirst().get();
Pair<SSLConfig, SSLSocketFactory> pair = sslSettingPerDC.get(toBeDeleteDataNodes.get(0).getDatacenterName());
ConnectedChannel channel1 = getBlockingChannelBasedOnPortType(portType, toBeDeleteDataNodes.get(0), pair.getSecond(), pair.getFirst());
channel1.connect();
ConnectedChannel channel2 = getBlockingChannelBasedOnPortType(portType, toBeDeleteDataNodes.get(1), pair.getSecond(), pair.getFirst());
channel2.connect();
DeleteRequest deleteRequest1 = new DeleteRequest(1, "deleteClient", blobId1, System.currentTimeMillis());
DataInputStream stream = channel1.sendAndReceive(deleteRequest1).getInputStream();
DeleteResponse deleteResponse = DeleteResponse.readFrom(stream);
releaseNettyBufUnderneathStream(stream);
assertEquals(ServerErrorCode.No_Error, deleteResponse.getError());
DeleteRequest deleteRequest2 = new DeleteRequest(1, "deleteClient", blobId1, deleteRequest1.getDeletionTimeInMs());
stream = channel2.sendAndReceive(deleteRequest2).getInputStream();
deleteResponse = DeleteResponse.readFrom(stream);
releaseNettyBufUnderneathStream(stream);
assertEquals(ServerErrorCode.No_Error, deleteResponse.getError());
// Now send the undelete operation through router, and it should fail because of not deleted error.
Future<Void> future = router.undeleteBlob(blobId1.toString(), "service");
try {
future.get();
fail("Undelete blob " + blobId1.toString() + " should fail");
} catch (ExecutionException e) {
assertTrue(e.getCause() instanceof RouterException);
assertEquals(RouterErrorCode.BlobNotDeleted, ((RouterException) e.getCause()).getErrorCode());
}
// Now see if either data node 1 or data node 2 has undelete or not, if so, undelete would replicate. If not,
// delete would replicate.
List<PartitionRequestInfo> partitionRequestInfoList = getPartitionRequestInfoListFromBlobId(blobId1);
boolean hasUndelete = false;
for (ConnectedChannel connectedChannel : new ConnectedChannel[] { channel1, channel2 }) {
GetRequest getRequest = new GetRequest(1, "clientId1", MessageFormatFlags.BlobProperties, partitionRequestInfoList, GetOption.Include_All);
stream = channel1.sendAndReceive(getRequest).getInputStream();
GetResponse getResponse = GetResponse.readFrom(stream, clusterMap);
assertEquals(ServerErrorCode.No_Error, getResponse.getPartitionResponseInfoList().get(0).getErrorCode());
MessageFormatRecord.deserializeBlobProperties(getResponse.getInputStream());
hasUndelete = getResponse.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).getLifeVersion() == (short) 1;
if (hasUndelete) {
break;
}
}
releaseNettyBufUnderneathStream(stream);
// Now restart the replication
controlReplicationForPartition(channels, partitionId, true);
if (hasUndelete) {
notificationSystem.awaitBlobUndeletes(blobId1.toString());
} else {
notificationSystem.awaitBlobDeletions(blobId1.toString());
}
for (ConnectedChannel connectedChannel : channels) {
GetRequest getRequest = new GetRequest(1, "clientId1", MessageFormatFlags.BlobProperties, partitionRequestInfoList, GetOption.Include_All);
stream = connectedChannel.sendAndReceive(getRequest).getInputStream();
GetResponse getResponse = GetResponse.readFrom(stream, clusterMap);
releaseNettyBufUnderneathStream(stream);
assertEquals(ServerErrorCode.No_Error, getResponse.getPartitionResponseInfoList().get(0).getErrorCode());
MessageFormatRecord.deserializeBlobProperties(getResponse.getInputStream());
if (hasUndelete) {
assertEquals((short) 1, getResponse.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).getLifeVersion());
assertTrue(getResponse.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).isUndeleted());
assertFalse(getResponse.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).isDeleted());
} else {
assertEquals((short) 0, getResponse.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).getLifeVersion());
assertTrue(getResponse.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).isDeleted());
assertFalse(getResponse.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).isUndeleted());
}
}
// ///////////////////////////////////////////////////////////
// Corner case 2: two data nodes have different life versions
// //////////////////////////////////////////////////////////
BlobId blobId2 = new BlobId(blobIdVersion, BlobId.BlobIdType.NATIVE, clusterMap.getLocalDatacenterId(), properties.getAccountId(), properties.getContainerId(), partitionIds.get(0), false, BlobId.BlobDataType.DATACHUNK);
putRequest = new PutRequest(1, "client1", blobId2, properties, ByteBuffer.wrap(userMetadata), Unpooled.wrappedBuffer(data), properties.getBlobSize(), BlobType.DataBlob, testEncryption ? ByteBuffer.wrap(encryptionKey) : null);
putResponseStream = channel.sendAndReceive(putRequest).getInputStream();
response = PutResponse.readFrom(putResponseStream);
releaseNettyBufUnderneathStream(putResponseStream);
assertEquals(ServerErrorCode.No_Error, response.getError());
notificationSystem.awaitBlobCreations(blobId2.toString());
// Now delete this blob on all servers.
DeleteRequest deleteRequest = new DeleteRequest(1, "deleteClient", blobId2, System.currentTimeMillis());
stream = channel.sendAndReceive(deleteRequest).getInputStream();
deleteResponse = DeleteResponse.readFrom(stream);
releaseNettyBufUnderneathStream(stream);
assertEquals(ServerErrorCode.No_Error, deleteResponse.getError());
notificationSystem.awaitBlobDeletions(blobId2.toString());
// Now stop the replication
partitionId = blobId2.getPartition();
controlReplicationForPartition(channels, partitionId, false);
// Now send the undelete to two data nodes in the same DC and then send delete
UndeleteRequest undeleteRequest = new UndeleteRequest(1, "undeleteClient", blobId2, System.currentTimeMillis());
stream = channel1.sendAndReceive(undeleteRequest).getInputStream();
UndeleteResponse undeleteResponse = UndeleteResponse.readFrom(stream);
releaseNettyBufUnderneathStream(stream);
assertEquals(ServerErrorCode.No_Error, undeleteResponse.getError());
assertEquals((short) 1, undeleteResponse.getLifeVersion());
undeleteRequest = new UndeleteRequest(1, "undeleteClient", blobId2, undeleteRequest.getOperationTimeMs());
stream = channel2.sendAndReceive(undeleteRequest).getInputStream();
undeleteResponse = UndeleteResponse.readFrom(stream);
releaseNettyBufUnderneathStream(stream);
assertEquals(ServerErrorCode.No_Error, undeleteResponse.getError());
assertEquals((short) 1, undeleteResponse.getLifeVersion());
deleteRequest1 = new DeleteRequest(1, "deleteClient", blobId2, System.currentTimeMillis());
stream = channel1.sendAndReceive(deleteRequest1).getInputStream();
deleteResponse = DeleteResponse.readFrom(stream);
releaseNettyBufUnderneathStream(stream);
assertEquals(ServerErrorCode.No_Error, deleteResponse.getError());
deleteRequest2 = new DeleteRequest(1, "deleteClient", blobId2, deleteRequest1.getDeletionTimeInMs());
stream = channel2.sendAndReceive(deleteRequest2).getInputStream();
deleteResponse = DeleteResponse.readFrom(stream);
releaseNettyBufUnderneathStream(stream);
assertEquals(ServerErrorCode.No_Error, deleteResponse.getError());
// Now send the undelete operation through router, and it should fail because of lifeVersion conflict error.
future = router.undeleteBlob(blobId2.toString(), "service");
try {
future.get();
fail("Undelete blob " + blobId2.toString() + " should fail");
} catch (ExecutionException e) {
assertTrue(e.getCause() instanceof RouterException);
assertEquals(RouterErrorCode.LifeVersionConflict, ((RouterException) e.getCause()).getErrorCode());
}
// Now restart the replication
controlReplicationForPartition(channels, partitionId, true);
notificationSystem.awaitBlobUndeletes(blobId2.toString());
// Now after replication is resumed, the undelete of lifeversion 2 will eventually be replicated to all servers.
partitionRequestInfoList = getPartitionRequestInfoListFromBlobId(blobId2);
for (ConnectedChannel connectedChannel : channels) {
// Even if the notificationSystem acknowledged the undelete, it might be triggered by undelete at lifeversion 1.
// So check in a loop with a time out.
long deadline = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(10);
while (true) {
GetRequest getRequest = new GetRequest(1, "clientId1", MessageFormatFlags.BlobProperties, partitionRequestInfoList, GetOption.Include_All);
stream = connectedChannel.sendAndReceive(getRequest).getInputStream();
GetResponse getResponse = GetResponse.readFrom(stream, clusterMap);
assertEquals(ServerErrorCode.No_Error, getResponse.getPartitionResponseInfoList().get(0).getErrorCode());
MessageFormatRecord.deserializeBlobProperties(getResponse.getInputStream());
if (getResponse.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).getLifeVersion() == 2) {
assertTrue(getResponse.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).isUndeleted());
assertFalse(getResponse.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).isDeleted());
break;
} else {
Thread.sleep(1000);
if (System.currentTimeMillis() > deadline) {
throw new TimeoutException("Fail to get blob " + blobId2 + " at lifeversion 2 at " + connectedChannel.getRemoteHost());
}
}
}
}
releaseNettyBufUnderneathStream(stream);
for (ConnectedChannel connectedChannel : channels) {
connectedChannel.disconnect();
}
channel1.disconnect();
channel2.disconnect();
channel.disconnect();
} catch (Exception e) {
e.printStackTrace();
fail();
} finally {
if (router != null) {
try {
router.close();
} catch (Exception e) {
}
}
}
}
Aggregations