Search in sources :

Example 26 with ClusterMap

use of com.github.ambry.clustermap.ClusterMap 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) {
            }
        }
    }
}
Also used : HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) UndeleteRequest(com.github.ambry.protocol.UndeleteRequest) UndeleteResponse(com.github.ambry.protocol.UndeleteResponse) InMemAccountService(com.github.ambry.account.InMemAccountService) GetRequest(com.github.ambry.protocol.GetRequest) ArrayList(java.util.ArrayList) List(java.util.List) SSLSocketFactory(javax.net.ssl.SSLSocketFactory) TimeoutException(java.util.concurrent.TimeoutException) VerifiableProperties(com.github.ambry.config.VerifiableProperties) Router(com.github.ambry.router.Router) ConnectedChannel(com.github.ambry.network.ConnectedChannel) MockPartitionId(com.github.ambry.clustermap.MockPartitionId) PartitionId(com.github.ambry.clustermap.PartitionId) DeleteResponse(com.github.ambry.protocol.DeleteResponse) DataNodeId(com.github.ambry.clustermap.DataNodeId) MockDataNodeId(com.github.ambry.clustermap.MockDataNodeId) InMemAccountService(com.github.ambry.account.InMemAccountService) AccountService(com.github.ambry.account.AccountService) Map(java.util.Map) HashMap(java.util.HashMap) ClusterMap(com.github.ambry.clustermap.ClusterMap) MockClusterMap(com.github.ambry.clustermap.MockClusterMap) MockClusterMap(com.github.ambry.clustermap.MockClusterMap) BlobProperties(com.github.ambry.messageformat.BlobProperties) Properties(java.util.Properties) VerifiableProperties(com.github.ambry.config.VerifiableProperties) PutResponse(com.github.ambry.protocol.PutResponse) ExecutionException(java.util.concurrent.ExecutionException) Pair(com.github.ambry.utils.Pair) NonBlockingRouterFactory(com.github.ambry.router.NonBlockingRouterFactory) SSLConfig(com.github.ambry.config.SSLConfig) RouterException(com.github.ambry.router.RouterException) PutRequest(com.github.ambry.protocol.PutRequest) NettyByteBufDataInputStream(com.github.ambry.utils.NettyByteBufDataInputStream) DataInputStream(java.io.DataInputStream) PartitionRequestInfo(com.github.ambry.protocol.PartitionRequestInfo) GetResponse(com.github.ambry.protocol.GetResponse) IOException(java.io.IOException) MessageFormatException(com.github.ambry.messageformat.MessageFormatException) RouterException(com.github.ambry.router.RouterException) ExecutionException(java.util.concurrent.ExecutionException) TimeoutException(java.util.concurrent.TimeoutException) BlobProperties(com.github.ambry.messageformat.BlobProperties) BlobId(com.github.ambry.commons.BlobId) DeleteRequest(com.github.ambry.protocol.DeleteRequest)

Example 27 with ClusterMap

use of com.github.ambry.clustermap.ClusterMap in project ambry by linkedin.

the class ServerTestUtil method endToEndReplicationWithMultiNodeMultiPartitionTest.

static void endToEndReplicationWithMultiNodeMultiPartitionTest(int interestedDataNodePortNumber, Port dataNode1Port, Port dataNode2Port, Port dataNode3Port, MockCluster cluster, SSLConfig clientSSLConfig1, SSLConfig clientSSLConfig2, SSLConfig clientSSLConfig3, SSLSocketFactory clientSSLSocketFactory1, SSLSocketFactory clientSSLSocketFactory2, SSLSocketFactory clientSSLSocketFactory3, MockNotificationSystem notificationSystem, boolean testEncryption) throws Exception {
    // interestedDataNodePortNumber is used to locate the datanode and hence has to be PlainTextPort
    MockClusterMap clusterMap = cluster.getClusterMap();
    BlobIdFactory blobIdFactory = new BlobIdFactory(clusterMap);
    List<AmbryServer> serverList = cluster.getServers();
    byte[] usermetadata = new byte[100];
    byte[] data = new byte[100];
    byte[] encryptionKey = null;
    short accountId = Utils.getRandomShort(TestUtils.RANDOM);
    short containerId = Utils.getRandomShort(TestUtils.RANDOM);
    BlobProperties properties = new BlobProperties(100, "serviceid1", null, null, false, TestUtils.TTL_SECS, cluster.time.milliseconds(), accountId, containerId, false, null, null, null);
    long expectedExpiryTimeMs = getExpiryTimeMs(properties);
    TestUtils.RANDOM.nextBytes(usermetadata);
    TestUtils.RANDOM.nextBytes(data);
    if (testEncryption) {
        encryptionKey = new byte[100];
        TestUtils.RANDOM.nextBytes(encryptionKey);
    }
    // connect to all the servers
    ConnectedChannel channel1 = getBlockingChannelBasedOnPortType(dataNode1Port, "localhost", clientSSLSocketFactory1, clientSSLConfig1);
    ConnectedChannel channel2 = getBlockingChannelBasedOnPortType(dataNode2Port, "localhost", clientSSLSocketFactory2, clientSSLConfig2);
    ConnectedChannel channel3 = getBlockingChannelBasedOnPortType(dataNode3Port, "localhost", clientSSLSocketFactory3, clientSSLConfig3);
    // put all the blobs to random servers
    channel1.connect();
    channel2.connect();
    channel3.connect();
    int noOfParallelThreads = 3;
    int totalBlobsToPut = 50;
    CountDownLatch latch = new CountDownLatch(noOfParallelThreads);
    List<DirectSender> runnables = new ArrayList<DirectSender>(noOfParallelThreads);
    ConnectedChannel channel = null;
    for (int i = 0; i < noOfParallelThreads; i++) {
        if (i % noOfParallelThreads == 0) {
            channel = channel1;
        } else if (i % noOfParallelThreads == 1) {
            channel = channel2;
        } else if (i % noOfParallelThreads == 2) {
            channel = channel3;
        }
        DirectSender runnable = new DirectSender(cluster, channel, totalBlobsToPut, data, usermetadata, properties, encryptionKey, latch);
        runnables.add(runnable);
        Thread threadToRun = new Thread(runnable);
        threadToRun.start();
    }
    assertTrue("Did not put all blobs in 2 minutes", latch.await(2, TimeUnit.MINUTES));
    // wait till replication can complete
    List<BlobId> blobIds = new ArrayList<BlobId>();
    for (int i = 0; i < runnables.size(); i++) {
        blobIds.addAll(runnables.get(i).getBlobIds());
    }
    for (BlobId blobId : blobIds) {
        notificationSystem.awaitBlobCreations(blobId.getID());
    }
    // Now that the blob is created and replicated, test the cases where a put request arrives for the same blob id
    // later than replication.
    testLatePutRequest(blobIds.get(0), properties, usermetadata, data, encryptionKey, channel1, channel2, channel3, ServerErrorCode.No_Error);
    // Test the case where a put arrives with the same id as one in the server, but the blob is not identical.
    BlobProperties differentProperties = new BlobProperties(properties.getBlobSize(), properties.getServiceId(), accountId, containerId, testEncryption, cluster.time.milliseconds());
    testLatePutRequest(blobIds.get(0), differentProperties, usermetadata, data, encryptionKey, channel1, channel2, channel3, ServerErrorCode.Blob_Already_Exists);
    byte[] differentUserMetadata = Arrays.copyOf(usermetadata, usermetadata.length);
    differentUserMetadata[0] = (byte) ~differentUserMetadata[0];
    testLatePutRequest(blobIds.get(0), properties, differentUserMetadata, data, encryptionKey, channel1, channel2, channel3, ServerErrorCode.Blob_Already_Exists);
    byte[] differentData = Arrays.copyOf(data, data.length);
    differentData[0] = (byte) ~differentData[0];
    testLatePutRequest(blobIds.get(0), properties, usermetadata, differentData, encryptionKey, channel1, channel2, channel3, ServerErrorCode.Blob_Already_Exists);
    // verify blob properties, metadata and blob across all nodes
    for (int i = 0; i < 3; i++) {
        channel = null;
        if (i == 0) {
            channel = channel1;
        } else if (i == 1) {
            channel = channel2;
        } else if (i == 2) {
            channel = channel3;
        }
        ArrayList<PartitionRequestInfo> partitionRequestInfoList = new ArrayList<PartitionRequestInfo>();
        for (int j = 0; j < blobIds.size(); j++) {
            ArrayList<BlobId> ids = new ArrayList<BlobId>();
            ids.add(blobIds.get(j));
            partitionRequestInfoList.clear();
            PartitionRequestInfo partitionRequestInfo = new PartitionRequestInfo(blobIds.get(j).getPartition(), ids);
            partitionRequestInfoList.add(partitionRequestInfo);
            GetRequest getRequest = new GetRequest(1, "clientid2", MessageFormatFlags.BlobProperties, partitionRequestInfoList, GetOption.None);
            DataInputStream stream = channel.sendAndReceive(getRequest).getInputStream();
            GetResponse resp = GetResponse.readFrom(stream, clusterMap);
            try {
                BlobProperties propertyOutput = MessageFormatRecord.deserializeBlobProperties(resp.getInputStream());
                assertEquals(100, propertyOutput.getBlobSize());
                assertEquals("serviceid1", propertyOutput.getServiceId());
                assertEquals("AccountId mismatch", accountId, propertyOutput.getAccountId());
                assertEquals("ContainerId mismatch", containerId, propertyOutput.getContainerId());
                assertEquals("Expiration time mismatch (props)", expectedExpiryTimeMs, getExpiryTimeMs(propertyOutput));
                assertEquals("Expiration time mismatch (MessageInfo)", expectedExpiryTimeMs, resp.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).getExpirationTimeInMs());
                releaseNettyBufUnderneathStream(stream);
            } catch (MessageFormatException e) {
                fail();
            }
            // get user metadata
            getRequest = new GetRequest(1, "clientid2", MessageFormatFlags.BlobUserMetadata, partitionRequestInfoList, GetOption.None);
            stream = channel.sendAndReceive(getRequest).getInputStream();
            resp = GetResponse.readFrom(stream, clusterMap);
            try {
                ByteBuffer userMetadataOutput = MessageFormatRecord.deserializeUserMetadata(resp.getInputStream());
                assertArrayEquals(usermetadata, userMetadataOutput.array());
                if (testEncryption) {
                    assertNotNull("MessageMetadata should not have been null", resp.getPartitionResponseInfoList().get(0).getMessageMetadataList().get(0));
                    assertArrayEquals("EncryptionKey mismatch", encryptionKey, resp.getPartitionResponseInfoList().get(0).getMessageMetadataList().get(0).getEncryptionKey().array());
                } else {
                    assertNull("MessageMetadata should have been null", resp.getPartitionResponseInfoList().get(0).getMessageMetadataList().get(0));
                }
                assertEquals("Expiration time mismatch (MessageInfo)", expectedExpiryTimeMs, resp.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).getExpirationTimeInMs());
            } catch (MessageFormatException e) {
                e.printStackTrace();
                fail();
            }
            releaseNettyBufUnderneathStream(stream);
            // get blob
            getRequest = new GetRequest(1, "clientid2", MessageFormatFlags.Blob, partitionRequestInfoList, GetOption.None);
            stream = channel.sendAndReceive(getRequest).getInputStream();
            resp = GetResponse.readFrom(stream, clusterMap);
            try {
                BlobData blobData = MessageFormatRecord.deserializeBlob(resp.getInputStream());
                byte[] blobout = getBlobDataAndRelease(blobData);
                assertArrayEquals(data, blobout);
                if (testEncryption) {
                    assertNotNull("MessageMetadata should not have been null", resp.getPartitionResponseInfoList().get(0).getMessageMetadataList().get(0));
                    assertArrayEquals("EncryptionKey mismatch", encryptionKey, resp.getPartitionResponseInfoList().get(0).getMessageMetadataList().get(0).getEncryptionKey().array());
                } else {
                    assertNull("MessageMetadata should have been null", resp.getPartitionResponseInfoList().get(0).getMessageMetadataList().get(0));
                }
                assertEquals("Expiration time mismatch (MessageInfo)", expectedExpiryTimeMs, resp.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).getExpirationTimeInMs());
            } catch (MessageFormatException e) {
                e.printStackTrace();
                fail();
            }
            releaseNettyBufUnderneathStream(stream);
            // get blob all
            getRequest = new GetRequest(1, "clientid2", MessageFormatFlags.All, partitionRequestInfoList, GetOption.None);
            stream = channel.sendAndReceive(getRequest).getInputStream();
            resp = GetResponse.readFrom(stream, clusterMap);
            try {
                BlobAll blobAll = MessageFormatRecord.deserializeBlobAll(resp.getInputStream(), blobIdFactory);
                assertEquals("Expiration time mismatch (props)", expectedExpiryTimeMs, getExpiryTimeMs(blobAll.getBlobInfo().getBlobProperties()));
                assertEquals("Expiration time mismatch (MessageInfo)", expectedExpiryTimeMs, resp.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).getExpirationTimeInMs());
                byte[] blobout = getBlobDataAndRelease(blobAll.getBlobData());
                assertArrayEquals(data, blobout);
                if (testEncryption) {
                    assertNotNull("EncryptionKey should not ne null", blobAll.getBlobEncryptionKey());
                    assertArrayEquals("EncryptionKey mismatch", encryptionKey, blobAll.getBlobEncryptionKey().array());
                } else {
                    assertNull("EncryptionKey should have been null", blobAll.getBlobEncryptionKey());
                }
                releaseNettyBufUnderneathStream(stream);
            } catch (MessageFormatException e) {
                e.printStackTrace();
                fail();
            }
        }
    }
    // ttl update all blobs and wait for replication
    Map<ConnectedChannel, List<BlobId>> channelToBlobIds = new HashMap<>();
    for (int i = 0; i < blobIds.size(); i++) {
        final BlobId blobId = blobIds.get(i);
        if (i % 3 == 0) {
            channelToBlobIds.computeIfAbsent(channel1, updateChannel -> new ArrayList<>()).add(blobId);
        } else if (i % 3 == 1) {
            channelToBlobIds.computeIfAbsent(channel2, updateChannel -> new ArrayList<>()).add(blobId);
        } else {
            channelToBlobIds.computeIfAbsent(channel3, updateChannel -> new ArrayList<>()).add(blobId);
        }
    }
    channelToBlobIds.entrySet().stream().map(entry -> CompletableFuture.supplyAsync(() -> {
        try {
            for (BlobId blobId : entry.getValue()) {
                updateBlobTtl(entry.getKey(), blobId, cluster.time.milliseconds());
            }
            return null;
        } catch (Throwable e) {
            throw new RuntimeException("Exception updating ttl for: " + entry, e);
        }
    })).forEach(CompletableFuture::join);
    // check that the TTL update has propagated
    blobIds.forEach(blobId -> notificationSystem.awaitBlobUpdates(blobId.getID(), UpdateType.TTL_UPDATE));
    // check all servers
    for (ConnectedChannel channelToUse : new ConnectedChannel[] { channel1, channel2, channel3 }) {
        for (BlobId blobId : blobIds) {
            checkTtlUpdateStatus(channelToUse, clusterMap, blobIdFactory, blobId, data, true, Utils.Infinite_Time);
        }
    }
    // delete random blobs, wait for replication and ensure it is deleted in all nodes
    Set<BlobId> blobsDeleted = new HashSet<BlobId>();
    Set<BlobId> blobsChecked = new HashSet<BlobId>();
    for (int i = 0; i < blobIds.size(); i++) {
        int j = new Random().nextInt(3);
        if (j == 0) {
            j = new Random().nextInt(3);
            if (j == 0) {
                channel = channel1;
            } else if (j == 1) {
                channel = channel2;
            } else if (j == 2) {
                channel = channel3;
            }
            DeleteRequest deleteRequest = new DeleteRequest(1, "reptest", blobIds.get(i), System.currentTimeMillis());
            DataInputStream deleteResponseStream = channel.sendAndReceive(deleteRequest).getInputStream();
            DeleteResponse deleteResponse = DeleteResponse.readFrom(deleteResponseStream);
            releaseNettyBufUnderneathStream(deleteResponseStream);
            assertEquals(ServerErrorCode.No_Error, deleteResponse.getError());
            blobsDeleted.add(blobIds.get(i));
        }
    }
    Iterator<BlobId> iterator = blobsDeleted.iterator();
    ArrayList<PartitionRequestInfo> partitionRequestInfoList = new ArrayList<PartitionRequestInfo>();
    while (iterator.hasNext()) {
        BlobId deletedId = iterator.next();
        notificationSystem.awaitBlobDeletions(deletedId.getID());
        for (int j = 0; j < 3; j++) {
            if (j == 0) {
                channel = channel1;
            } else if (j == 1) {
                channel = channel2;
            } else if (j == 2) {
                channel = channel3;
            }
            ArrayList<BlobId> ids = new ArrayList<BlobId>();
            ids.add(deletedId);
            partitionRequestInfoList.clear();
            PartitionRequestInfo partitionRequestInfo = new PartitionRequestInfo(deletedId.getPartition(), ids);
            partitionRequestInfoList.add(partitionRequestInfo);
            GetRequest getRequest = new GetRequest(1, "clientid2", MessageFormatFlags.Blob, partitionRequestInfoList, GetOption.None);
            DataInputStream stream = channel.sendAndReceive(getRequest).getInputStream();
            GetResponse resp = GetResponse.readFrom(stream, clusterMap);
            assertEquals(ServerErrorCode.Blob_Deleted, resp.getPartitionResponseInfoList().get(0).getErrorCode());
            releaseNettyBufUnderneathStream(stream);
        }
    }
    // take a server down, clean up a mount path, start and ensure replication fixes it
    serverList.get(0).shutdown();
    serverList.get(0).awaitShutdown();
    MockDataNodeId dataNode = (MockDataNodeId) clusterMap.getDataNodeId("localhost", interestedDataNodePortNumber);
    System.out.println("Cleaning mount path " + dataNode.getMountPaths().get(0));
    for (ReplicaId replicaId : clusterMap.getReplicaIds(dataNode)) {
        if (replicaId.getMountPath().compareToIgnoreCase(dataNode.getMountPaths().get(0)) == 0) {
            System.out.println("Cleaning partition " + replicaId.getPartitionId());
        }
    }
    deleteFolderContent(new File(dataNode.getMountPaths().get(0)), false);
    for (int i = 0; i < blobIds.size(); i++) {
        for (ReplicaId replicaId : blobIds.get(i).getPartition().getReplicaIds()) {
            if (replicaId.getMountPath().compareToIgnoreCase(dataNode.getMountPaths().get(0)) == 0) {
                if (blobsDeleted.contains(blobIds.get(i))) {
                    notificationSystem.decrementDeletedReplica(blobIds.get(i).getID(), dataNode.getHostname(), dataNode.getPort());
                } else {
                    notificationSystem.decrementCreatedReplica(blobIds.get(i).getID(), dataNode.getHostname(), dataNode.getPort());
                    notificationSystem.decrementUpdatedReplica(blobIds.get(i).getID(), dataNode.getHostname(), dataNode.getPort(), UpdateType.TTL_UPDATE);
                }
            }
        }
    }
    cluster.reinitServer(0);
    channel1.disconnect();
    channel1.connect();
    for (int j = 0; j < blobIds.size(); j++) {
        if (blobsDeleted.contains(blobIds.get(j))) {
            notificationSystem.awaitBlobDeletions(blobIds.get(j).getID());
        } else {
            notificationSystem.awaitBlobCreations(blobIds.get(j).getID());
            notificationSystem.awaitBlobUpdates(blobIds.get(j).getID(), UpdateType.TTL_UPDATE);
        }
        ArrayList<BlobId> ids = new ArrayList<BlobId>();
        ids.add(blobIds.get(j));
        partitionRequestInfoList.clear();
        PartitionRequestInfo partitionRequestInfo = new PartitionRequestInfo(blobIds.get(j).getPartition(), ids);
        partitionRequestInfoList.add(partitionRequestInfo);
        GetRequest getRequest = new GetRequest(1, "clientid2", MessageFormatFlags.BlobProperties, partitionRequestInfoList, GetOption.None);
        DataInputStream stream = channel1.sendAndReceive(getRequest).getInputStream();
        GetResponse resp = GetResponse.readFrom(stream, clusterMap);
        if (resp.getPartitionResponseInfoList().get(0).getErrorCode() == ServerErrorCode.Blob_Deleted || resp.getPartitionResponseInfoList().get(0).getErrorCode() == ServerErrorCode.Blob_Not_Found) {
            assertTrue(blobsDeleted.contains(blobIds.get(j)));
        } else {
            try {
                BlobProperties propertyOutput = MessageFormatRecord.deserializeBlobProperties(resp.getInputStream());
                assertEquals(100, propertyOutput.getBlobSize());
                assertEquals("serviceid1", propertyOutput.getServiceId());
                assertEquals("AccountId mismatch", accountId, propertyOutput.getAccountId());
                assertEquals("ContainerId mismatch", containerId, propertyOutput.getContainerId());
                assertEquals("Expiration time mismatch in MessageInfo", Utils.Infinite_Time, resp.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).getExpirationTimeInMs());
            } catch (MessageFormatException e) {
                fail();
            }
        }
        releaseNettyBufUnderneathStream(stream);
        // get user metadata
        getRequest = new GetRequest(1, "clientid2", MessageFormatFlags.BlobUserMetadata, partitionRequestInfoList, GetOption.None);
        stream = channel1.sendAndReceive(getRequest).getInputStream();
        resp = GetResponse.readFrom(stream, clusterMap);
        if (resp.getPartitionResponseInfoList().get(0).getErrorCode() == ServerErrorCode.Blob_Deleted || resp.getPartitionResponseInfoList().get(0).getErrorCode() == ServerErrorCode.Blob_Not_Found) {
            assertTrue(blobsDeleted.contains(blobIds.get(j)));
        } else {
            try {
                ByteBuffer userMetadataOutput = MessageFormatRecord.deserializeUserMetadata(resp.getInputStream());
                assertArrayEquals(usermetadata, userMetadataOutput.array());
                if (testEncryption) {
                    assertNotNull("MessageMetadata should not have been null", resp.getPartitionResponseInfoList().get(0).getMessageMetadataList().get(0));
                    assertArrayEquals("EncryptionKey mismatch", encryptionKey, resp.getPartitionResponseInfoList().get(0).getMessageMetadataList().get(0).getEncryptionKey().array());
                } else {
                    assertNull("MessageMetadata should have been null", resp.getPartitionResponseInfoList().get(0).getMessageMetadataList().get(0));
                }
                assertEquals("Expiration time mismatch in MessageInfo", Utils.Infinite_Time, resp.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).getExpirationTimeInMs());
            } catch (MessageFormatException e) {
                fail();
            }
        }
        releaseNettyBufUnderneathStream(stream);
        // get blob
        getRequest = new GetRequest(1, "clientid2", MessageFormatFlags.Blob, partitionRequestInfoList, GetOption.None);
        stream = channel1.sendAndReceive(getRequest).getInputStream();
        resp = GetResponse.readFrom(stream, clusterMap);
        if (resp.getPartitionResponseInfoList().get(0).getErrorCode() == ServerErrorCode.Blob_Deleted || resp.getPartitionResponseInfoList().get(0).getErrorCode() == ServerErrorCode.Blob_Not_Found) {
            assertTrue(blobsDeleted.contains(blobIds.get(j)));
        } else {
            try {
                BlobData blobData = MessageFormatRecord.deserializeBlob(resp.getInputStream());
                byte[] blobout = getBlobDataAndRelease(blobData);
                assertArrayEquals(data, blobout);
                if (testEncryption) {
                    assertNotNull("MessageMetadata should not have been null", resp.getPartitionResponseInfoList().get(0).getMessageMetadataList().get(0));
                    assertArrayEquals("EncryptionKey mismatch", encryptionKey, resp.getPartitionResponseInfoList().get(0).getMessageMetadataList().get(0).getEncryptionKey().array());
                } else {
                    assertNull("MessageMetadata should have been null", resp.getPartitionResponseInfoList().get(0).getMessageMetadataList().get(0));
                }
                assertEquals("Expiration time mismatch in MessageInfo", Utils.Infinite_Time, resp.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).getExpirationTimeInMs());
            } catch (MessageFormatException e) {
                fail();
            }
        }
        releaseNettyBufUnderneathStream(stream);
        // get blob all
        getRequest = new GetRequest(1, "clientid2", MessageFormatFlags.All, partitionRequestInfoList, GetOption.None);
        stream = channel1.sendAndReceive(getRequest).getInputStream();
        resp = GetResponse.readFrom(stream, clusterMap);
        if (resp.getPartitionResponseInfoList().get(0).getErrorCode() == ServerErrorCode.Blob_Deleted || resp.getPartitionResponseInfoList().get(0).getErrorCode() == ServerErrorCode.Blob_Not_Found) {
            assertTrue(blobsDeleted.contains(blobIds.get(j)));
            blobsDeleted.remove(blobIds.get(j));
            blobsChecked.add(blobIds.get(j));
        } else {
            try {
                BlobAll blobAll = MessageFormatRecord.deserializeBlobAll(resp.getInputStream(), blobIdFactory);
                byte[] blobout = getBlobDataAndRelease(blobAll.getBlobData());
                assertArrayEquals(data, blobout);
                if (testEncryption) {
                    assertNotNull("EncryptionKey should not ne null", blobAll.getBlobEncryptionKey());
                    assertArrayEquals("EncryptionKey mismatch", encryptionKey, blobAll.getBlobEncryptionKey().array());
                } else {
                    assertNull("EncryptionKey should have been null", blobAll.getBlobEncryptionKey());
                }
                assertEquals("Expiration time mismatch in MessageInfo", Utils.Infinite_Time, resp.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).getExpirationTimeInMs());
            } catch (MessageFormatException e) {
                fail();
            }
        }
        releaseNettyBufUnderneathStream(stream);
    }
    assertEquals(0, blobsDeleted.size());
    // take a server down, clean all contents, start and ensure replication fixes it
    serverList.get(0).shutdown();
    serverList.get(0).awaitShutdown();
    dataNode = (MockDataNodeId) clusterMap.getDataNodeId("localhost", interestedDataNodePortNumber);
    for (int i = 0; i < dataNode.getMountPaths().size(); i++) {
        System.out.println("Cleaning mount path " + dataNode.getMountPaths().get(i));
        for (ReplicaId replicaId : clusterMap.getReplicaIds(dataNode)) {
            if (replicaId.getMountPath().compareToIgnoreCase(dataNode.getMountPaths().get(i)) == 0) {
                System.out.println("Cleaning partition " + replicaId.getPartitionId());
            }
        }
        deleteFolderContent(new File(dataNode.getMountPaths().get(i)), false);
    }
    for (int i = 0; i < blobIds.size(); i++) {
        if (blobsChecked.contains(blobIds.get(i))) {
            notificationSystem.decrementDeletedReplica(blobIds.get(i).getID(), dataNode.getHostname(), dataNode.getPort());
        } else {
            notificationSystem.decrementCreatedReplica(blobIds.get(i).getID(), dataNode.getHostname(), dataNode.getPort());
        }
    }
    cluster.reinitServer(0);
    channel1.disconnect();
    channel1.connect();
    for (int j = 0; j < blobIds.size(); j++) {
        if (blobsChecked.contains(blobIds.get(j))) {
            notificationSystem.awaitBlobDeletions(blobIds.get(j).getID());
        } else {
            notificationSystem.awaitBlobCreations(blobIds.get(j).getID());
        }
        ArrayList<BlobId> ids = new ArrayList<BlobId>();
        ids.add(blobIds.get(j));
        partitionRequestInfoList.clear();
        PartitionRequestInfo partitionRequestInfo = new PartitionRequestInfo(blobIds.get(j).getPartition(), ids);
        partitionRequestInfoList.add(partitionRequestInfo);
        GetRequest getRequest = new GetRequest(1, "clientid2", MessageFormatFlags.BlobProperties, partitionRequestInfoList, GetOption.None);
        DataInputStream stream = channel1.sendAndReceive(getRequest).getInputStream();
        GetResponse resp = GetResponse.readFrom(stream, clusterMap);
        if (resp.getPartitionResponseInfoList().get(0).getErrorCode() == ServerErrorCode.Blob_Deleted || resp.getPartitionResponseInfoList().get(0).getErrorCode() == ServerErrorCode.Blob_Not_Found) {
            assertTrue(blobsChecked.contains(blobIds.get(j)));
        } else {
            try {
                BlobProperties propertyOutput = MessageFormatRecord.deserializeBlobProperties(resp.getInputStream());
                assertEquals(100, propertyOutput.getBlobSize());
                assertEquals("serviceid1", propertyOutput.getServiceId());
                assertEquals("AccountId mismatch", accountId, propertyOutput.getAccountId());
                assertEquals("ContainerId mismatch", containerId, propertyOutput.getContainerId());
            } catch (MessageFormatException e) {
                fail();
            }
        }
        releaseNettyBufUnderneathStream(stream);
        // get user metadata
        getRequest = new GetRequest(1, "clientid2", MessageFormatFlags.BlobUserMetadata, partitionRequestInfoList, GetOption.None);
        stream = channel1.sendAndReceive(getRequest).getInputStream();
        resp = GetResponse.readFrom(stream, clusterMap);
        if (resp.getPartitionResponseInfoList().get(0).getErrorCode() == ServerErrorCode.Blob_Deleted || resp.getPartitionResponseInfoList().get(0).getErrorCode() == ServerErrorCode.Blob_Not_Found) {
            assertTrue(blobsChecked.contains(blobIds.get(j)));
        } else {
            try {
                ByteBuffer userMetadataOutput = MessageFormatRecord.deserializeUserMetadata(resp.getInputStream());
                assertArrayEquals(usermetadata, userMetadataOutput.array());
                if (testEncryption) {
                    assertNotNull("MessageMetadata should not have been null", resp.getPartitionResponseInfoList().get(0).getMessageMetadataList().get(0));
                    assertArrayEquals("EncryptionKey mismatch", encryptionKey, resp.getPartitionResponseInfoList().get(0).getMessageMetadataList().get(0).getEncryptionKey().array());
                } else {
                    assertNull("MessageMetadata should have been null", resp.getPartitionResponseInfoList().get(0).getMessageMetadataList().get(0));
                }
            } catch (MessageFormatException e) {
                fail();
            }
        }
        releaseNettyBufUnderneathStream(stream);
        // get blob
        getRequest = new GetRequest(1, "clientid2", MessageFormatFlags.Blob, partitionRequestInfoList, GetOption.None);
        stream = channel1.sendAndReceive(getRequest).getInputStream();
        resp = GetResponse.readFrom(stream, clusterMap);
        if (resp.getPartitionResponseInfoList().get(0).getErrorCode() == ServerErrorCode.Blob_Deleted || resp.getPartitionResponseInfoList().get(0).getErrorCode() == ServerErrorCode.Blob_Not_Found) {
            assertTrue(blobsChecked.contains(blobIds.get(j)));
        } else {
            try {
                BlobData blobData = MessageFormatRecord.deserializeBlob(resp.getInputStream());
                byte[] blobout = getBlobDataAndRelease(blobData);
                assertArrayEquals(data, blobout);
                if (testEncryption) {
                    assertNotNull("MessageMetadata should not have been null", resp.getPartitionResponseInfoList().get(0).getMessageMetadataList().get(0));
                    assertArrayEquals("EncryptionKey mismatch", encryptionKey, resp.getPartitionResponseInfoList().get(0).getMessageMetadataList().get(0).getEncryptionKey().array());
                } else {
                    assertNull("MessageMetadata should have been null", resp.getPartitionResponseInfoList().get(0).getMessageMetadataList().get(0));
                }
            } catch (MessageFormatException e) {
                fail();
            }
        }
        releaseNettyBufUnderneathStream(stream);
        // get blob all
        getRequest = new GetRequest(1, "clientid2", MessageFormatFlags.All, partitionRequestInfoList, GetOption.None);
        stream = channel1.sendAndReceive(getRequest).getInputStream();
        resp = GetResponse.readFrom(stream, clusterMap);
        if (resp.getPartitionResponseInfoList().get(0).getErrorCode() == ServerErrorCode.Blob_Deleted || resp.getPartitionResponseInfoList().get(0).getErrorCode() == ServerErrorCode.Blob_Not_Found) {
            assertTrue(blobsChecked.contains(blobIds.get(j)));
            blobsChecked.remove(blobIds.get(j));
            blobsDeleted.add(blobIds.get(j));
        } else {
            try {
                BlobAll blobAll = MessageFormatRecord.deserializeBlobAll(resp.getInputStream(), blobIdFactory);
                byte[] blobout = getBlobDataAndRelease(blobAll.getBlobData());
                assertArrayEquals(data, blobout);
                if (testEncryption) {
                    assertNotNull("EncryptionKey should not ne null", blobAll.getBlobEncryptionKey());
                    assertArrayEquals("EncryptionKey mismatch", encryptionKey, blobAll.getBlobEncryptionKey().array());
                } else {
                    assertNull("EncryptionKey should have been null", blobAll.getBlobEncryptionKey());
                }
            } catch (MessageFormatException e) {
                fail();
            }
        }
        releaseNettyBufUnderneathStream(stream);
    }
    assertEquals(0, blobsChecked.size());
    short expectedLifeVersion = 1;
    for (int i = 0; i < 2; i++) {
        expectedLifeVersion += i;
        // First undelete all deleted blobs
        for (BlobId deletedId : blobsDeleted) {
            UndeleteRequest undeleteRequest = new UndeleteRequest(2, "reptest", deletedId, System.currentTimeMillis());
            DataInputStream undeleteResponseStream = channel3.sendAndReceive(undeleteRequest).getInputStream();
            UndeleteResponse undeleteResponse = UndeleteResponse.readFrom(undeleteResponseStream);
            releaseNettyBufUnderneathStream(undeleteResponseStream);
            assertEquals(ServerErrorCode.No_Error, undeleteResponse.getError());
            assertEquals(expectedLifeVersion, undeleteResponse.getLifeVersion());
        }
        Thread.sleep(5000);
        // Then use get request to get all the data back and make sure the lifeVersion is correct
        for (BlobId id : blobsDeleted) {
            // We don't need to wait for blob undeletes, since one of the hosts has Put Record deleted
            // from disk, so undelete this blob would end up replicating Put Record instead of undelete.
            // notificationSystem.awaitBlobUndeletes(id.toString());
            ArrayList<BlobId> ids = new ArrayList<>();
            ids.add(id);
            partitionRequestInfoList.clear();
            PartitionRequestInfo partitionRequestInfo = new PartitionRequestInfo(id.getPartition(), ids);
            partitionRequestInfoList.add(partitionRequestInfo);
            // get blob all
            GetRequest getRequest = new GetRequest(1, "clientid20", MessageFormatFlags.All, partitionRequestInfoList, GetOption.None);
            DataInputStream stream = channel1.sendAndReceive(getRequest).getInputStream();
            GetResponse resp = GetResponse.readFrom(stream, clusterMap);
            assertEquals(ServerErrorCode.No_Error, resp.getError());
            assertEquals(1, resp.getPartitionResponseInfoList().size());
            assertEquals(ServerErrorCode.No_Error, resp.getPartitionResponseInfoList().get(0).getErrorCode());
            assertEquals(1, resp.getPartitionResponseInfoList().get(0).getMessageInfoList().size());
            MessageInfo info = resp.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0);
            assertEquals(expectedLifeVersion, info.getLifeVersion());
            assertFalse(info.isDeleted());
            try {
                BlobAll blobAll = MessageFormatRecord.deserializeBlobAll(resp.getInputStream(), blobIdFactory);
                byte[] blobout = getBlobDataAndRelease(blobAll.getBlobData());
                assertArrayEquals(data, blobout);
                if (testEncryption) {
                    assertNotNull("EncryptionKey should not ne null", blobAll.getBlobEncryptionKey());
                    assertArrayEquals("EncryptionKey mismatch", encryptionKey, blobAll.getBlobEncryptionKey().array());
                } else {
                    assertNull("EncryptionKey should have been null", blobAll.getBlobEncryptionKey());
                }
                assertEquals("Expiration time mismatch in MessageInfo", Utils.Infinite_Time, resp.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).getExpirationTimeInMs());
                releaseNettyBufUnderneathStream(stream);
            } catch (MessageFormatException e) {
                fail();
            }
        }
        for (BlobId id : blobsDeleted) {
            DeleteRequest deleteRequest = new DeleteRequest(1, "reptest", id, System.currentTimeMillis());
            DataInputStream deleteResponseStream = channel.sendAndReceive(deleteRequest).getInputStream();
            DeleteResponse deleteResponse = DeleteResponse.readFrom(deleteResponseStream);
            releaseNettyBufUnderneathStream(deleteResponseStream);
            assertEquals(ServerErrorCode.No_Error, deleteResponse.getError());
        }
        Thread.sleep(1000);
        for (BlobId id : blobsDeleted) {
            ArrayList<BlobId> ids = new ArrayList<>();
            ids.add(id);
            partitionRequestInfoList.clear();
            PartitionRequestInfo partitionRequestInfo = new PartitionRequestInfo(id.getPartition(), ids);
            partitionRequestInfoList.add(partitionRequestInfo);
            // get blob all
            GetRequest getRequest = new GetRequest(1, "clientid200", MessageFormatFlags.All, partitionRequestInfoList, GetOption.Include_All);
            DataInputStream stream = channel1.sendAndReceive(getRequest).getInputStream();
            GetResponse resp = GetResponse.readFrom(stream, clusterMap);
            assertEquals(ServerErrorCode.No_Error, resp.getError());
            assertEquals(1, resp.getPartitionResponseInfoList().size());
            assertEquals(ServerErrorCode.No_Error, resp.getPartitionResponseInfoList().get(0).getErrorCode());
            assertEquals(1, resp.getPartitionResponseInfoList().get(0).getMessageInfoList().size());
            MessageInfo info = resp.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0);
            assertEquals(expectedLifeVersion, info.getLifeVersion());
            assertTrue(info.isDeleted());
            try {
                BlobAll blobAll = MessageFormatRecord.deserializeBlobAll(resp.getInputStream(), blobIdFactory);
                byte[] blobout = getBlobDataAndRelease(blobAll.getBlobData());
                assertArrayEquals(data, blobout);
                if (testEncryption) {
                    assertNotNull("EncryptionKey should not ne null", blobAll.getBlobEncryptionKey());
                    assertArrayEquals("EncryptionKey mismatch", encryptionKey, blobAll.getBlobEncryptionKey().array());
                } else {
                    assertNull("EncryptionKey should have been null", blobAll.getBlobEncryptionKey());
                }
                assertEquals("Expiration time mismatch in MessageInfo", Utils.Infinite_Time, resp.getPartitionResponseInfoList().get(0).getMessageInfoList().get(0).getExpirationTimeInMs());
                releaseNettyBufUnderneathStream(stream);
            } catch (MessageFormatException e) {
                fail();
            }
        }
    }
    channel1.disconnect();
    channel2.disconnect();
    channel3.disconnect();
}
Also used : AdminRequestOrResponseType(com.github.ambry.protocol.AdminRequestOrResponseType) GetOption(com.github.ambry.protocol.GetOption) Arrays(java.util.Arrays) Http2ClientConfig(com.github.ambry.config.Http2ClientConfig) BlobProperties(com.github.ambry.messageformat.BlobProperties) BlobAll(com.github.ambry.messageformat.BlobAll) SubRecord(com.github.ambry.messageformat.SubRecord) Future(java.util.concurrent.Future) TestUtils(com.github.ambry.utils.TestUtils) Map(java.util.Map) UndeleteRequest(com.github.ambry.protocol.UndeleteRequest) TtlUpdateResponse(com.github.ambry.protocol.TtlUpdateResponse) RetainingAsyncWritableChannel(com.github.ambry.commons.RetainingAsyncWritableChannel) RouterErrorCode(com.github.ambry.router.RouterErrorCode) ReadableStreamChannel(com.github.ambry.router.ReadableStreamChannel) SSLFactory(com.github.ambry.commons.SSLFactory) BlobData(com.github.ambry.messageformat.BlobData) Set(java.util.Set) MockPartitionId(com.github.ambry.clustermap.MockPartitionId) SSLSocketFactory(javax.net.ssl.SSLSocketFactory) CountDownLatch(java.util.concurrent.CountDownLatch) MockDiskId(com.github.ambry.clustermap.MockDiskId) UpdateType(com.github.ambry.notification.UpdateType) PartitionRequestInfo(com.github.ambry.protocol.PartitionRequestInfo) ArrayList(java.util.ArrayList) NettyByteBufDataInputStream(com.github.ambry.utils.NettyByteBufDataInputStream) ReplicationControlAdminRequest(com.github.ambry.protocol.ReplicationControlAdminRequest) BlockingChannel(com.github.ambry.network.BlockingChannel) BlockingChannelConnectionPool(com.github.ambry.network.BlockingChannelConnectionPool) MetricRegistry(com.codahale.metrics.MetricRegistry) Properties(java.util.Properties) PutMessageFormatInputStream(com.github.ambry.messageformat.PutMessageFormatInputStream) Offset(com.github.ambry.store.Offset) VerifiableProperties(com.github.ambry.config.VerifiableProperties) IOException(java.io.IOException) BlobIdFactory(com.github.ambry.commons.BlobIdFactory) MessageFormatException(com.github.ambry.messageformat.MessageFormatException) PutResponse(com.github.ambry.protocol.PutResponse) RouterException(com.github.ambry.router.RouterException) LatchBasedInMemoryCloudDestinationFactory(com.github.ambry.cloud.LatchBasedInMemoryCloudDestinationFactory) File(java.io.File) ExecutionException(java.util.concurrent.ExecutionException) GetBlobResult(com.github.ambry.router.GetBlobResult) ReplicaId(com.github.ambry.clustermap.ReplicaId) PutBlobOptionsBuilder(com.github.ambry.router.PutBlobOptionsBuilder) ByteBufferInputStream(com.github.ambry.utils.ByteBufferInputStream) ClusterMapConfig(com.github.ambry.config.ClusterMapConfig) BlobStoreControlAdminRequest(com.github.ambry.protocol.BlobStoreControlAdminRequest) StoreFindToken(com.github.ambry.store.StoreFindToken) Assert(org.junit.Assert) GetBlobOptionsBuilder(com.github.ambry.router.GetBlobOptionsBuilder) Http2BlockingChannel(com.github.ambry.network.http2.Http2BlockingChannel) ConnectionPoolConfig(com.github.ambry.config.ConnectionPoolConfig) DataNodeId(com.github.ambry.clustermap.DataNodeId) ByteBufferReadableStreamChannel(com.github.ambry.commons.ByteBufferReadableStreamChannel) TimeoutException(java.util.concurrent.TimeoutException) Http2ClientMetrics(com.github.ambry.network.http2.Http2ClientMetrics) Random(java.util.Random) ByteBuffer(java.nio.ByteBuffer) Unpooled(io.netty.buffer.Unpooled) MockReplicaId(com.github.ambry.clustermap.MockReplicaId) GetResponse(com.github.ambry.protocol.GetResponse) PortType(com.github.ambry.network.PortType) BlobStoreControlAction(com.github.ambry.protocol.BlobStoreControlAction) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) SSLBlockingChannel(com.github.ambry.network.SSLBlockingChannel) GetRequest(com.github.ambry.protocol.GetRequest) VcrServer(com.github.ambry.cloud.VcrServer) StoreKeyFactory(com.github.ambry.store.StoreKeyFactory) Utils(com.github.ambry.utils.Utils) LinkedBlockingQueue(java.util.concurrent.LinkedBlockingQueue) Collectors(java.util.stream.Collectors) ConnectedChannel(com.github.ambry.network.ConnectedChannel) NettySslHttp2Factory(com.github.ambry.commons.NettySslHttp2Factory) Objects(java.util.Objects) RouterConfig(com.github.ambry.config.RouterConfig) List(java.util.List) TtlUpdateRequest(com.github.ambry.protocol.TtlUpdateRequest) MessageFormatFlags(com.github.ambry.messageformat.MessageFormatFlags) QuotaTestUtils(com.github.ambry.quota.QuotaTestUtils) Callback(com.github.ambry.commons.Callback) BlobType(com.github.ambry.messageformat.BlobType) InMemAccountService(com.github.ambry.account.InMemAccountService) PartitionId(com.github.ambry.clustermap.PartitionId) BlobId(com.github.ambry.commons.BlobId) DataInputStream(java.io.DataInputStream) FindTokenFactory(com.github.ambry.replication.FindTokenFactory) CloudDestinationFactory(com.github.ambry.cloud.CloudDestinationFactory) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) AccountService(com.github.ambry.account.AccountService) UndeleteResponse(com.github.ambry.protocol.UndeleteResponse) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) HardwareState(com.github.ambry.clustermap.HardwareState) AdminResponse(com.github.ambry.protocol.AdminResponse) AtomicReference(java.util.concurrent.atomic.AtomicReference) CrcInputStream(com.github.ambry.utils.CrcInputStream) HashSet(java.util.HashSet) AdminRequest(com.github.ambry.protocol.AdminRequest) SSLConfig(com.github.ambry.config.SSLConfig) ByteBuf(io.netty.buffer.ByteBuf) LatchBasedInMemoryCloudDestination(com.github.ambry.cloud.LatchBasedInMemoryCloudDestination) DeleteResponse(com.github.ambry.protocol.DeleteResponse) CommonTestUtils(com.github.ambry.commons.CommonTestUtils) HelixControllerManager(com.github.ambry.utils.HelixControllerManager) PutRequest(com.github.ambry.protocol.PutRequest) Router(com.github.ambry.router.Router) MockDataNodeId(com.github.ambry.clustermap.MockDataNodeId) VcrTestUtil(com.github.ambry.cloud.VcrTestUtil) DeleteRequest(com.github.ambry.protocol.DeleteRequest) Pair(com.github.ambry.utils.Pair) Iterator(java.util.Iterator) ClusterAgentsFactory(com.github.ambry.clustermap.ClusterAgentsFactory) ReplicaType(com.github.ambry.clustermap.ReplicaType) ConnectionPool(com.github.ambry.network.ConnectionPool) QuotaChargeCallback(com.github.ambry.quota.QuotaChargeCallback) ClusterMap(com.github.ambry.clustermap.ClusterMap) FileInputStream(java.io.FileInputStream) NonBlockingRouterFactory(com.github.ambry.router.NonBlockingRouterFactory) TimeUnit(java.util.concurrent.TimeUnit) MessageInfo(com.github.ambry.store.MessageInfo) MessageFormatRecord(com.github.ambry.messageformat.MessageFormatRecord) Port(com.github.ambry.network.Port) CloudBlobMetadata(com.github.ambry.cloud.CloudBlobMetadata) Collections(java.util.Collections) MockClusterMap(com.github.ambry.clustermap.MockClusterMap) InputStream(java.io.InputStream) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) UndeleteRequest(com.github.ambry.protocol.UndeleteRequest) UndeleteResponse(com.github.ambry.protocol.UndeleteResponse) CompletableFuture(java.util.concurrent.CompletableFuture) BlobAll(com.github.ambry.messageformat.BlobAll) Random(java.util.Random) GetRequest(com.github.ambry.protocol.GetRequest) BlobData(com.github.ambry.messageformat.BlobData) ArrayList(java.util.ArrayList) List(java.util.List) HashSet(java.util.HashSet) MessageFormatException(com.github.ambry.messageformat.MessageFormatException) ConnectedChannel(com.github.ambry.network.ConnectedChannel) CountDownLatch(java.util.concurrent.CountDownLatch) PartitionRequestInfo(com.github.ambry.protocol.PartitionRequestInfo) NettyByteBufDataInputStream(com.github.ambry.utils.NettyByteBufDataInputStream) DataInputStream(java.io.DataInputStream) GetResponse(com.github.ambry.protocol.GetResponse) ByteBuffer(java.nio.ByteBuffer) ReplicaId(com.github.ambry.clustermap.ReplicaId) MockReplicaId(com.github.ambry.clustermap.MockReplicaId) BlobIdFactory(com.github.ambry.commons.BlobIdFactory) MessageInfo(com.github.ambry.store.MessageInfo) DeleteResponse(com.github.ambry.protocol.DeleteResponse) BlobProperties(com.github.ambry.messageformat.BlobProperties) MockDataNodeId(com.github.ambry.clustermap.MockDataNodeId) BlobId(com.github.ambry.commons.BlobId) DeleteRequest(com.github.ambry.protocol.DeleteRequest) File(java.io.File) MockClusterMap(com.github.ambry.clustermap.MockClusterMap)

Example 28 with ClusterMap

use of com.github.ambry.clustermap.ClusterMap in project ambry by linkedin.

the class ServerTestUtil method undeleteRecoveryTest.

static void undeleteRecoveryTest(Port targetPort, MockCluster cluster, SSLConfig clientSSLConfig, SSLSocketFactory clientSSLSocketFactory) {
    try {
        MockClusterMap clusterMap = cluster.getClusterMap();
        byte[] userMetadata = new byte[1000];
        byte[] data = new byte[31870];
        short accountId = Utils.getRandomShort(TestUtils.RANDOM);
        short containerId = Utils.getRandomShort(TestUtils.RANDOM);
        BlobProperties properties = new BlobProperties(31870, "serviceid1", accountId, containerId, false, cluster.time.milliseconds());
        TestUtils.RANDOM.nextBytes(userMetadata);
        TestUtils.RANDOM.nextBytes(data);
        List<PartitionId> partitionIds = clusterMap.getWritablePartitionIds(MockClusterMap.DEFAULT_PARTITION_CLASS);
        short blobIdVersion = CommonTestUtils.getCurrentBlobIdVersion();
        BlobId blobId1 = new BlobId(blobIdVersion, BlobId.BlobIdType.NATIVE, clusterMap.getLocalDatacenterId(), properties.getAccountId(), properties.getContainerId(), partitionIds.get(0), false, BlobId.BlobDataType.DATACHUNK);
        // put blob 1
        PutRequest putRequest = new PutRequest(1, "client1", blobId1, properties, ByteBuffer.wrap(userMetadata), Unpooled.wrappedBuffer(data), properties.getBlobSize(), BlobType.DataBlob, null);
        ConnectedChannel channel = getBlockingChannelBasedOnPortType(targetPort, "localhost", clientSSLSocketFactory, clientSSLConfig);
        channel.connect();
        DataInputStream stream = channel.sendAndReceive(putRequest).getInputStream();
        PutResponse response = PutResponse.readFrom(stream);
        releaseNettyBufUnderneathStream(stream);
        assertEquals(ServerErrorCode.No_Error, response.getError());
        for (int i = 0; i < 2; i++) {
            // delete blob 1
            deleteBlob(channel, blobId1, cluster.time.milliseconds());
            // undelete blob 1
            undeleteBlob(channel, blobId1, cluster.time.milliseconds(), (short) (i + 1));
        }
        // put blob 2 that is expired (Add additional 5 secs to avoid Blob_Update_Not_Allowed failure as TtlUpdate op time
        // is also cluster.time.milliseconds(). Theoretically, it should succeed as op time = expiry time - buffer time.
        // However, the index value converts ms to sec when putting a blob, so the milliseconds part of initial put blob
        // time is wiped out and makes op time > expiry time - buffer time. Adding some time should avoid this failure.)
        long ttl = 24 * 60 * 60 + 5;
        BlobProperties propertiesExpired = new BlobProperties(31870, "serviceid1", "ownerid", "jpeg", false, ttl, cluster.time.milliseconds(), accountId, containerId, false, null, null, null);
        BlobId blobId2 = new BlobId(blobIdVersion, BlobId.BlobIdType.NATIVE, clusterMap.getLocalDatacenterId(), propertiesExpired.getAccountId(), propertiesExpired.getContainerId(), partitionIds.get(0), false, BlobId.BlobDataType.DATACHUNK);
        PutRequest putRequest2 = new PutRequest(1, "client1", blobId2, propertiesExpired, ByteBuffer.wrap(userMetadata), Unpooled.wrappedBuffer(data), properties.getBlobSize(), BlobType.DataBlob, null);
        stream = channel.sendAndReceive(putRequest2).getInputStream();
        PutResponse response2 = PutResponse.readFrom(stream);
        releaseNettyBufUnderneathStream(stream);
        assertEquals(ServerErrorCode.No_Error, response2.getError());
        for (int i = 0; i < 2; i++) {
            // delete blob 2
            deleteBlob(channel, blobId2, cluster.time.milliseconds());
            // undelete blob 2
            undeleteBlob(channel, blobId2, cluster.time.milliseconds(), (short) (i + 1));
        }
        // ttl update blob 2
        updateBlobTtl(channel, blobId2, cluster.time.milliseconds());
        cluster.time.sleep(ttl + 10000);
        // Now stops the server and remove all the index files for this partition and test its recovery.
        channel.disconnect();
        AmbryServer server = cluster.getServers().get(0);
        server.shutdown();
        server.awaitShutdown();
        MockDataNodeId dataNode = (MockDataNodeId) clusterMap.getDataNodeId("localhost", channel.getRemotePort());
        for (ReplicaId replica : partitionIds.get(0).getReplicaIds()) {
            if (replica.getDataNodeId().equals(dataNode)) {
                for (File file : Objects.requireNonNull(new File(replica.getReplicaPath()).listFiles((file, filename) -> filename.endsWith("index")))) {
                    file.delete();
                }
            }
        }
        cluster.reinitServer(0);
        channel = getBlockingChannelBasedOnPortType(targetPort, "localhost", clientSSLSocketFactory, clientSSLConfig);
        channel.connect();
        // Now verify that we can fetch blob1 and blob2.
        for (BlobId blobId : new BlobId[] { blobId1, blobId2 }) {
            long deadline = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(10);
            while (true) {
                // get blob properties
                ArrayList<BlobId> ids = new ArrayList<>();
                ids.add(blobId);
                ArrayList<PartitionRequestInfo> partitionRequestInfoList = new ArrayList<>();
                PartitionRequestInfo partitionRequestInfo = new PartitionRequestInfo(partitionIds.get(0), ids);
                partitionRequestInfoList.add(partitionRequestInfo);
                GetRequest getRequest = new GetRequest(1, "clientid2", MessageFormatFlags.BlobProperties, partitionRequestInfoList, GetOption.None);
                stream = channel.sendAndReceive(getRequest).getInputStream();
                GetResponse getResponse = GetResponse.readFrom(stream, clusterMap);
                if (getResponse.getPartitionResponseInfoList().get(0).getErrorCode() == ServerErrorCode.No_Error) {
                    BlobProperties propertyOutput = MessageFormatRecord.deserializeBlobProperties(getResponse.getInputStream());
                    assertEquals(31870, propertyOutput.getBlobSize());
                    assertEquals("serviceid1", propertyOutput.getServiceId());
                    assertEquals("AccountId mismatch", accountId, propertyOutput.getAccountId());
                    assertEquals("ContainerId mismatch", containerId, propertyOutput.getContainerId());
                    releaseNettyBufUnderneathStream(stream);
                    break;
                } else {
                    Thread.sleep(1000);
                    if (System.currentTimeMillis() > deadline) {
                        throw new TimeoutException("Fail to get blob " + blobId + " at  " + channel.getRemoteHost());
                    }
                }
            }
        }
        channel.disconnect();
    } catch (Exception e) {
        e.printStackTrace();
        fail();
    }
}
Also used : AdminRequestOrResponseType(com.github.ambry.protocol.AdminRequestOrResponseType) GetOption(com.github.ambry.protocol.GetOption) Arrays(java.util.Arrays) Http2ClientConfig(com.github.ambry.config.Http2ClientConfig) BlobProperties(com.github.ambry.messageformat.BlobProperties) BlobAll(com.github.ambry.messageformat.BlobAll) SubRecord(com.github.ambry.messageformat.SubRecord) Future(java.util.concurrent.Future) TestUtils(com.github.ambry.utils.TestUtils) Map(java.util.Map) UndeleteRequest(com.github.ambry.protocol.UndeleteRequest) TtlUpdateResponse(com.github.ambry.protocol.TtlUpdateResponse) RetainingAsyncWritableChannel(com.github.ambry.commons.RetainingAsyncWritableChannel) RouterErrorCode(com.github.ambry.router.RouterErrorCode) ReadableStreamChannel(com.github.ambry.router.ReadableStreamChannel) SSLFactory(com.github.ambry.commons.SSLFactory) BlobData(com.github.ambry.messageformat.BlobData) Set(java.util.Set) MockPartitionId(com.github.ambry.clustermap.MockPartitionId) SSLSocketFactory(javax.net.ssl.SSLSocketFactory) CountDownLatch(java.util.concurrent.CountDownLatch) MockDiskId(com.github.ambry.clustermap.MockDiskId) UpdateType(com.github.ambry.notification.UpdateType) PartitionRequestInfo(com.github.ambry.protocol.PartitionRequestInfo) ArrayList(java.util.ArrayList) NettyByteBufDataInputStream(com.github.ambry.utils.NettyByteBufDataInputStream) ReplicationControlAdminRequest(com.github.ambry.protocol.ReplicationControlAdminRequest) BlockingChannel(com.github.ambry.network.BlockingChannel) BlockingChannelConnectionPool(com.github.ambry.network.BlockingChannelConnectionPool) MetricRegistry(com.codahale.metrics.MetricRegistry) Properties(java.util.Properties) PutMessageFormatInputStream(com.github.ambry.messageformat.PutMessageFormatInputStream) Offset(com.github.ambry.store.Offset) VerifiableProperties(com.github.ambry.config.VerifiableProperties) IOException(java.io.IOException) BlobIdFactory(com.github.ambry.commons.BlobIdFactory) MessageFormatException(com.github.ambry.messageformat.MessageFormatException) PutResponse(com.github.ambry.protocol.PutResponse) RouterException(com.github.ambry.router.RouterException) LatchBasedInMemoryCloudDestinationFactory(com.github.ambry.cloud.LatchBasedInMemoryCloudDestinationFactory) File(java.io.File) ExecutionException(java.util.concurrent.ExecutionException) GetBlobResult(com.github.ambry.router.GetBlobResult) ReplicaId(com.github.ambry.clustermap.ReplicaId) PutBlobOptionsBuilder(com.github.ambry.router.PutBlobOptionsBuilder) ByteBufferInputStream(com.github.ambry.utils.ByteBufferInputStream) ClusterMapConfig(com.github.ambry.config.ClusterMapConfig) BlobStoreControlAdminRequest(com.github.ambry.protocol.BlobStoreControlAdminRequest) StoreFindToken(com.github.ambry.store.StoreFindToken) Assert(org.junit.Assert) GetBlobOptionsBuilder(com.github.ambry.router.GetBlobOptionsBuilder) Http2BlockingChannel(com.github.ambry.network.http2.Http2BlockingChannel) ConnectionPoolConfig(com.github.ambry.config.ConnectionPoolConfig) DataNodeId(com.github.ambry.clustermap.DataNodeId) ByteBufferReadableStreamChannel(com.github.ambry.commons.ByteBufferReadableStreamChannel) TimeoutException(java.util.concurrent.TimeoutException) Http2ClientMetrics(com.github.ambry.network.http2.Http2ClientMetrics) Random(java.util.Random) ByteBuffer(java.nio.ByteBuffer) Unpooled(io.netty.buffer.Unpooled) MockReplicaId(com.github.ambry.clustermap.MockReplicaId) GetResponse(com.github.ambry.protocol.GetResponse) PortType(com.github.ambry.network.PortType) BlobStoreControlAction(com.github.ambry.protocol.BlobStoreControlAction) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) SSLBlockingChannel(com.github.ambry.network.SSLBlockingChannel) GetRequest(com.github.ambry.protocol.GetRequest) VcrServer(com.github.ambry.cloud.VcrServer) StoreKeyFactory(com.github.ambry.store.StoreKeyFactory) Utils(com.github.ambry.utils.Utils) LinkedBlockingQueue(java.util.concurrent.LinkedBlockingQueue) Collectors(java.util.stream.Collectors) ConnectedChannel(com.github.ambry.network.ConnectedChannel) NettySslHttp2Factory(com.github.ambry.commons.NettySslHttp2Factory) Objects(java.util.Objects) RouterConfig(com.github.ambry.config.RouterConfig) List(java.util.List) TtlUpdateRequest(com.github.ambry.protocol.TtlUpdateRequest) MessageFormatFlags(com.github.ambry.messageformat.MessageFormatFlags) QuotaTestUtils(com.github.ambry.quota.QuotaTestUtils) Callback(com.github.ambry.commons.Callback) BlobType(com.github.ambry.messageformat.BlobType) InMemAccountService(com.github.ambry.account.InMemAccountService) PartitionId(com.github.ambry.clustermap.PartitionId) BlobId(com.github.ambry.commons.BlobId) DataInputStream(java.io.DataInputStream) FindTokenFactory(com.github.ambry.replication.FindTokenFactory) CloudDestinationFactory(com.github.ambry.cloud.CloudDestinationFactory) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) AccountService(com.github.ambry.account.AccountService) UndeleteResponse(com.github.ambry.protocol.UndeleteResponse) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) HardwareState(com.github.ambry.clustermap.HardwareState) AdminResponse(com.github.ambry.protocol.AdminResponse) AtomicReference(java.util.concurrent.atomic.AtomicReference) CrcInputStream(com.github.ambry.utils.CrcInputStream) HashSet(java.util.HashSet) AdminRequest(com.github.ambry.protocol.AdminRequest) SSLConfig(com.github.ambry.config.SSLConfig) ByteBuf(io.netty.buffer.ByteBuf) LatchBasedInMemoryCloudDestination(com.github.ambry.cloud.LatchBasedInMemoryCloudDestination) DeleteResponse(com.github.ambry.protocol.DeleteResponse) CommonTestUtils(com.github.ambry.commons.CommonTestUtils) HelixControllerManager(com.github.ambry.utils.HelixControllerManager) PutRequest(com.github.ambry.protocol.PutRequest) Router(com.github.ambry.router.Router) MockDataNodeId(com.github.ambry.clustermap.MockDataNodeId) VcrTestUtil(com.github.ambry.cloud.VcrTestUtil) DeleteRequest(com.github.ambry.protocol.DeleteRequest) Pair(com.github.ambry.utils.Pair) Iterator(java.util.Iterator) ClusterAgentsFactory(com.github.ambry.clustermap.ClusterAgentsFactory) ReplicaType(com.github.ambry.clustermap.ReplicaType) ConnectionPool(com.github.ambry.network.ConnectionPool) QuotaChargeCallback(com.github.ambry.quota.QuotaChargeCallback) ClusterMap(com.github.ambry.clustermap.ClusterMap) FileInputStream(java.io.FileInputStream) NonBlockingRouterFactory(com.github.ambry.router.NonBlockingRouterFactory) TimeUnit(java.util.concurrent.TimeUnit) MessageInfo(com.github.ambry.store.MessageInfo) MessageFormatRecord(com.github.ambry.messageformat.MessageFormatRecord) Port(com.github.ambry.network.Port) CloudBlobMetadata(com.github.ambry.cloud.CloudBlobMetadata) Collections(java.util.Collections) MockClusterMap(com.github.ambry.clustermap.MockClusterMap) InputStream(java.io.InputStream) ArrayList(java.util.ArrayList) PutResponse(com.github.ambry.protocol.PutResponse) GetRequest(com.github.ambry.protocol.GetRequest) TimeoutException(java.util.concurrent.TimeoutException) PutRequest(com.github.ambry.protocol.PutRequest) ConnectedChannel(com.github.ambry.network.ConnectedChannel) MockPartitionId(com.github.ambry.clustermap.MockPartitionId) PartitionId(com.github.ambry.clustermap.PartitionId) NettyByteBufDataInputStream(com.github.ambry.utils.NettyByteBufDataInputStream) DataInputStream(java.io.DataInputStream) PartitionRequestInfo(com.github.ambry.protocol.PartitionRequestInfo) GetResponse(com.github.ambry.protocol.GetResponse) ReplicaId(com.github.ambry.clustermap.ReplicaId) MockReplicaId(com.github.ambry.clustermap.MockReplicaId) IOException(java.io.IOException) MessageFormatException(com.github.ambry.messageformat.MessageFormatException) RouterException(com.github.ambry.router.RouterException) ExecutionException(java.util.concurrent.ExecutionException) TimeoutException(java.util.concurrent.TimeoutException) BlobProperties(com.github.ambry.messageformat.BlobProperties) MockDataNodeId(com.github.ambry.clustermap.MockDataNodeId) BlobId(com.github.ambry.commons.BlobId) File(java.io.File) MockClusterMap(com.github.ambry.clustermap.MockClusterMap)

Example 29 with ClusterMap

use of com.github.ambry.clustermap.ClusterMap in project ambry by linkedin.

the class CloudTokenPersistorTest method basicTest.

@Test
public void basicTest() throws Exception {
    Properties props = VcrTestUtil.createVcrProperties("DC1", "vcrClusterName", "zkConnectString", 12310, 12410, 12510, null);
    props.setProperty("replication.cloud.token.factory", replicationCloudTokenFactory);
    CloudConfig cloudConfig = new CloudConfig(new VerifiableProperties(props));
    ClusterMapConfig clusterMapConfig = new ClusterMapConfig(new VerifiableProperties(props));
    ClusterMap clusterMap = new MockClusterMap();
    DataNodeId dataNodeId = new CloudDataNode(cloudConfig, clusterMapConfig);
    Map<String, Set<PartitionInfo>> mountPathToPartitionInfoList = new HashMap<>();
    BlobIdFactory blobIdFactory = new BlobIdFactory(clusterMap);
    StoreFindTokenFactory factory = new StoreFindTokenFactory(blobIdFactory);
    PartitionId partitionId = clusterMap.getAllPartitionIds(null).get(0);
    ReplicaId cloudReplicaId = new CloudReplica(partitionId, dataNodeId);
    List<? extends ReplicaId> peerReplicas = cloudReplicaId.getPeerReplicaIds();
    List<RemoteReplicaInfo> remoteReplicas = new ArrayList<RemoteReplicaInfo>();
    List<RemoteReplicaInfo.ReplicaTokenInfo> replicaTokenInfos = new ArrayList<>();
    for (ReplicaId remoteReplica : peerReplicas) {
        RemoteReplicaInfo remoteReplicaInfo = new RemoteReplicaInfo(remoteReplica, cloudReplicaId, null, factory.getNewFindToken(), 10, SystemTime.getInstance(), remoteReplica.getDataNodeId().getPortToConnectTo());
        remoteReplicas.add(remoteReplicaInfo);
        replicaTokenInfos.add(new RemoteReplicaInfo.ReplicaTokenInfo(remoteReplicaInfo));
    }
    PartitionInfo partitionInfo = new PartitionInfo(remoteReplicas, partitionId, null, cloudReplicaId);
    mountPathToPartitionInfoList.computeIfAbsent(cloudReplicaId.getMountPath(), key -> ConcurrentHashMap.newKeySet()).add(partitionInfo);
    LatchBasedInMemoryCloudDestination cloudDestination = new LatchBasedInMemoryCloudDestination(Collections.emptyList(), AzureCloudDestinationFactory.getReplicationFeedType(new VerifiableProperties(props)), clusterMap);
    ReplicationConfig replicationConfig = new ReplicationConfig(new VerifiableProperties(props));
    CloudTokenPersistor cloudTokenPersistor = new CloudTokenPersistor("replicaTokens", mountPathToPartitionInfoList, new ReplicationMetrics(new MetricRegistry(), Collections.emptyList()), clusterMap, new FindTokenHelper(blobIdFactory, replicationConfig), cloudDestination);
    cloudTokenPersistor.persist(cloudReplicaId.getMountPath(), replicaTokenInfos);
    List<RemoteReplicaInfo.ReplicaTokenInfo> retrievedReplicaTokenInfos = cloudTokenPersistor.retrieve(cloudReplicaId.getMountPath());
    Assert.assertEquals("Number of tokens doesn't match.", replicaTokenInfos.size(), retrievedReplicaTokenInfos.size());
    for (int i = 0; i < replicaTokenInfos.size(); i++) {
        Assert.assertArrayEquals("Token is not correct.", replicaTokenInfos.get(i).getReplicaToken().toBytes(), retrievedReplicaTokenInfos.get(i).getReplicaToken().toBytes());
    }
}
Also used : Arrays(java.util.Arrays) AzureCloudDestinationFactory(com.github.ambry.cloud.azure.AzureCloudDestinationFactory) CloudReplica(com.github.ambry.clustermap.CloudReplica) DataNodeId(com.github.ambry.clustermap.DataNodeId) ReplicationMetrics(com.github.ambry.replication.ReplicationMetrics) RunWith(org.junit.runner.RunWith) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) CloudConfig(com.github.ambry.config.CloudConfig) Map(java.util.Map) SystemTime(com.github.ambry.utils.SystemTime) RemoteReplicaInfo(com.github.ambry.replication.RemoteReplicaInfo) CloudDataNode(com.github.ambry.clustermap.CloudDataNode) PartitionInfo(com.github.ambry.replication.PartitionInfo) Parameterized(org.junit.runners.Parameterized) ReplicationConfig(com.github.ambry.config.ReplicationConfig) FindTokenHelper(com.github.ambry.replication.FindTokenHelper) MetricRegistry(com.codahale.metrics.MetricRegistry) Properties(java.util.Properties) VerifiableProperties(com.github.ambry.config.VerifiableProperties) Collection(java.util.Collection) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Set(java.util.Set) ClusterMap(com.github.ambry.clustermap.ClusterMap) Test(org.junit.Test) BlobIdFactory(com.github.ambry.commons.BlobIdFactory) List(java.util.List) ReplicaId(com.github.ambry.clustermap.ReplicaId) StoreFindTokenFactory(com.github.ambry.store.StoreFindTokenFactory) ClusterMapConfig(com.github.ambry.config.ClusterMapConfig) Assert(org.junit.Assert) Collections(java.util.Collections) MockClusterMap(com.github.ambry.clustermap.MockClusterMap) PartitionId(com.github.ambry.clustermap.PartitionId) ClusterMap(com.github.ambry.clustermap.ClusterMap) MockClusterMap(com.github.ambry.clustermap.MockClusterMap) Set(java.util.Set) RemoteReplicaInfo(com.github.ambry.replication.RemoteReplicaInfo) HashMap(java.util.HashMap) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) ReplicationMetrics(com.github.ambry.replication.ReplicationMetrics) CloudConfig(com.github.ambry.config.CloudConfig) ArrayList(java.util.ArrayList) Properties(java.util.Properties) VerifiableProperties(com.github.ambry.config.VerifiableProperties) PartitionInfo(com.github.ambry.replication.PartitionInfo) CloudReplica(com.github.ambry.clustermap.CloudReplica) StoreFindTokenFactory(com.github.ambry.store.StoreFindTokenFactory) ReplicationConfig(com.github.ambry.config.ReplicationConfig) VerifiableProperties(com.github.ambry.config.VerifiableProperties) FindTokenHelper(com.github.ambry.replication.FindTokenHelper) MetricRegistry(com.codahale.metrics.MetricRegistry) PartitionId(com.github.ambry.clustermap.PartitionId) CloudDataNode(com.github.ambry.clustermap.CloudDataNode) ClusterMapConfig(com.github.ambry.config.ClusterMapConfig) ReplicaId(com.github.ambry.clustermap.ReplicaId) BlobIdFactory(com.github.ambry.commons.BlobIdFactory) DataNodeId(com.github.ambry.clustermap.DataNodeId) MockClusterMap(com.github.ambry.clustermap.MockClusterMap) Test(org.junit.Test)

Example 30 with ClusterMap

use of com.github.ambry.clustermap.ClusterMap in project ambry by linkedin.

the class ReplicationTest method replicaThreadTestConverter.

/**
 * Tests that replication between a local and remote server who have different
 * blob IDs for the same blobs (via StoreKeyConverter)
 * @throws Exception
 */
@Test
public void replicaThreadTestConverter() throws Exception {
    MockClusterMap clusterMap = new MockClusterMap();
    Pair<MockHost, MockHost> localAndRemoteHosts = getLocalAndRemoteHosts(clusterMap);
    MockHost localHost = localAndRemoteHosts.getFirst();
    MockHost remoteHost = localAndRemoteHosts.getSecond();
    MockHost expectedLocalHost = new MockHost(localHost.dataNodeId, clusterMap);
    MockStoreKeyConverterFactory storeKeyConverterFactory = new MockStoreKeyConverterFactory(null, null);
    storeKeyConverterFactory.setConversionMap(new HashMap<>());
    storeKeyConverterFactory.setReturnInputIfAbsent(true);
    MockStoreKeyConverterFactory.MockStoreKeyConverter storeKeyConverter = storeKeyConverterFactory.getStoreKeyConverter();
    List<PartitionId> partitionIds = clusterMap.getWritablePartitionIds(null);
    Map<PartitionId, List<StoreKey>> idsToBeIgnoredByPartition = new HashMap<>();
    StoreKeyFactory storeKeyFactory = new BlobIdFactory(clusterMap);
    Transformer transformer = new BlobIdTransformer(storeKeyFactory, storeKeyConverter);
    int batchSize = 4;
    Pair<Map<DataNodeId, List<RemoteReplicaInfo>>, ReplicaThread> replicasAndThread = getRemoteReplicasAndReplicaThread(batchSize, clusterMap, localHost, remoteHost, storeKeyConverter, transformer, null, null);
    Map<DataNodeId, List<RemoteReplicaInfo>> replicasToReplicate = replicasAndThread.getFirst();
    ReplicaThread replicaThread = replicasAndThread.getSecond();
    /*
        STORE KEY CONVERTER MAPPING
        Key     Value
        B0      B0'
        B1      B1'
        B2      null

        BEFORE
        Local   Remote
        B0'     B0
                B1
                B2

        AFTER
        Local   Remote
        B0'     B0
        B1'     B1
                B2
        B0 is B0' for local,
        B1 is B1' for local,
        B2 is null for local,
        so it already has B0/B0'
        B1 is transferred to B1'
        and B2 is invalid for L
        so it does not count as missing
        Missing Keys: 1
    */
    Map<PartitionId, List<BlobId>> partitionIdToDeleteBlobId = new HashMap<>();
    Map<StoreKey, StoreKey> conversionMap = new HashMap<>();
    Map<PartitionId, BlobId> expectedPartitionIdToDeleteBlobId = new HashMap<>();
    for (int i = 0; i < partitionIds.size(); i++) {
        PartitionId partitionId = partitionIds.get(i);
        List<BlobId> deleteBlobIds = new ArrayList<>();
        partitionIdToDeleteBlobId.put(partitionId, deleteBlobIds);
        BlobId b0 = generateRandomBlobId(partitionId);
        deleteBlobIds.add(b0);
        BlobId b0p = generateRandomBlobId(partitionId);
        expectedPartitionIdToDeleteBlobId.put(partitionId, b0p);
        BlobId b1 = generateRandomBlobId(partitionId);
        BlobId b1p = generateRandomBlobId(partitionId);
        BlobId b2 = generateRandomBlobId(partitionId);
        deleteBlobIds.add(b2);
        conversionMap.put(b0, b0p);
        conversionMap.put(b1, b1p);
        conversionMap.put(b2, null);
        // Convert current conversion map so that BlobIdTransformer can
        // create b1p in expectedLocalHost
        storeKeyConverter.setConversionMap(conversionMap);
        storeKeyConverter.convert(conversionMap.keySet());
        addPutMessagesToReplicasOfPartition(Arrays.asList(b0p), Arrays.asList(localHost));
        addPutMessagesToReplicasOfPartition(Arrays.asList(b0, b1, b2), Arrays.asList(remoteHost));
        addPutMessagesToReplicasOfPartition(Arrays.asList(b0p, b1), Arrays.asList(null, transformer), Arrays.asList(expectedLocalHost));
        // Check that expected local host contains the correct blob ids
        Set<BlobId> expectedLocalHostBlobIds = new HashSet<>();
        expectedLocalHostBlobIds.add(b0p);
        expectedLocalHostBlobIds.add(b1p);
        for (MessageInfo messageInfo : expectedLocalHost.infosByPartition.get(partitionId)) {
            assertTrue("Remove should never fail", expectedLocalHostBlobIds.remove(messageInfo.getStoreKey()));
        }
        assertTrue("expectedLocalHostBlobIds should now be empty", expectedLocalHostBlobIds.isEmpty());
    }
    storeKeyConverter.setConversionMap(conversionMap);
    int expectedIndex = assertMissingKeysAndFixMissingStoreKeys(0, 2, batchSize, 1, replicaThread, remoteHost, replicasToReplicate);
    // Check that there are no missing buffers between expectedLocalHost and LocalHost
    Map<PartitionId, List<ByteBuffer>> missingBuffers = expectedLocalHost.getMissingBuffers(localHost.buffersByPartition);
    assertTrue(missingBuffers.isEmpty());
    missingBuffers = localHost.getMissingBuffers(expectedLocalHost.buffersByPartition);
    assertTrue(missingBuffers.isEmpty());
    // delete blob
    for (int i = 0; i < partitionIds.size(); i++) {
        PartitionId partitionId = partitionIds.get(i);
        List<BlobId> deleteBlobIds = partitionIdToDeleteBlobId.get(partitionId);
        for (BlobId deleteBlobId : deleteBlobIds) {
            addDeleteMessagesToReplicasOfPartition(partitionId, deleteBlobId, Arrays.asList(remoteHost));
        }
        addDeleteMessagesToReplicasOfPartition(partitionId, expectedPartitionIdToDeleteBlobId.get(partitionId), Arrays.asList(expectedLocalHost));
    }
    expectedIndex = assertMissingKeysAndFixMissingStoreKeys(expectedIndex, 2, batchSize, 0, replicaThread, remoteHost, replicasToReplicate);
    // Check that there are no missing buffers between expectedLocalHost and LocalHost
    missingBuffers = expectedLocalHost.getMissingBuffers(localHost.buffersByPartition);
    assertTrue(missingBuffers.isEmpty());
    missingBuffers = localHost.getMissingBuffers(expectedLocalHost.buffersByPartition);
    assertTrue(missingBuffers.isEmpty());
    // 3 unconverted + 2 unconverted deleted expected missing buffers
    verifyNoMoreMissingKeysAndExpectedMissingBufferCount(remoteHost, localHost, replicaThread, replicasToReplicate, idsToBeIgnoredByPartition, storeKeyConverter, expectedIndex, expectedIndex, 5);
}
Also used : ValidatingTransformer(com.github.ambry.messageformat.ValidatingTransformer) Transformer(com.github.ambry.store.Transformer) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) StoreKeyFactory(com.github.ambry.store.StoreKeyFactory) List(java.util.List) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) MockStoreKeyConverterFactory(com.github.ambry.store.MockStoreKeyConverterFactory) MockPartitionId(com.github.ambry.clustermap.MockPartitionId) PartitionId(com.github.ambry.clustermap.PartitionId) StoreKey(com.github.ambry.store.StoreKey) BlobIdFactory(com.github.ambry.commons.BlobIdFactory) MessageInfo(com.github.ambry.store.MessageInfo) Map(java.util.Map) HashMap(java.util.HashMap) ClusterMap(com.github.ambry.clustermap.ClusterMap) MockClusterMap(com.github.ambry.clustermap.MockClusterMap) DataNodeId(com.github.ambry.clustermap.DataNodeId) MockDataNodeId(com.github.ambry.clustermap.MockDataNodeId) BlobId(com.github.ambry.commons.BlobId) MockClusterMap(com.github.ambry.clustermap.MockClusterMap) Test(org.junit.Test)

Aggregations

ClusterMap (com.github.ambry.clustermap.ClusterMap)51 VerifiableProperties (com.github.ambry.config.VerifiableProperties)37 MockClusterMap (com.github.ambry.clustermap.MockClusterMap)32 ArrayList (java.util.ArrayList)29 Properties (java.util.Properties)27 ClusterMapConfig (com.github.ambry.config.ClusterMapConfig)25 HashMap (java.util.HashMap)25 Map (java.util.Map)24 Test (org.junit.Test)24 PartitionId (com.github.ambry.clustermap.PartitionId)23 BlobIdFactory (com.github.ambry.commons.BlobIdFactory)23 List (java.util.List)23 MockPartitionId (com.github.ambry.clustermap.MockPartitionId)20 DataNodeId (com.github.ambry.clustermap.DataNodeId)19 StoreKeyFactory (com.github.ambry.store.StoreKeyFactory)18 ClusterAgentsFactory (com.github.ambry.clustermap.ClusterAgentsFactory)17 BlobId (com.github.ambry.commons.BlobId)17 MetricRegistry (com.codahale.metrics.MetricRegistry)16 MockStoreKeyConverterFactory (com.github.ambry.store.MockStoreKeyConverterFactory)16 File (java.io.File)16