Search in sources :

Example 1 with ChannelOutput

use of com.github.ambry.network.ChannelOutput in project ambry by linkedin.

the class ReplicaThread method getMessagesForMissingKeys.

/**
 * Gets the messages for the keys that are missing from the local store by issuing a {@link GetRequest} to the remote
 * node, if there are any missing keys. If there are no missing keys to be fetched, then no request is issued and a
 * null response is returned.
 * @param connectedChannel The connection channel to the remote node
 * @param exchangeMetadataResponseList The list of metadata response from the remote node
 * @param replicasToReplicatePerNode The list of remote replicas for the remote node
 * @param remoteNode The remote node from which replication needs to happen
 * @return The response that contains the missing messages; or null if no request was issued because there were no
 * keys missing.
 * @throws ReplicationException
 * @throws IOException
 */
private GetResponse getMessagesForMissingKeys(ConnectedChannel connectedChannel, List<ExchangeMetadataResponse> exchangeMetadataResponseList, List<RemoteReplicaInfo> replicasToReplicatePerNode, DataNodeId remoteNode) throws ReplicationException, IOException {
    List<PartitionRequestInfo> partitionRequestInfoList = new ArrayList<PartitionRequestInfo>();
    for (int i = 0; i < exchangeMetadataResponseList.size(); i++) {
        ExchangeMetadataResponse exchangeMetadataResponse = exchangeMetadataResponseList.get(i);
        RemoteReplicaInfo remoteReplicaInfo = replicasToReplicatePerNode.get(i);
        if (exchangeMetadataResponse.serverErrorCode == ServerErrorCode.No_Error) {
            Set<StoreKey> missingStoreKeys = exchangeMetadataResponse.missingStoreKeys;
            if (missingStoreKeys.size() > 0) {
                ArrayList<BlobId> keysToFetch = new ArrayList<BlobId>();
                for (StoreKey storeKey : missingStoreKeys) {
                    keysToFetch.add((BlobId) storeKey);
                }
                PartitionRequestInfo partitionRequestInfo = new PartitionRequestInfo(remoteReplicaInfo.getReplicaId().getPartitionId(), keysToFetch);
                partitionRequestInfoList.add(partitionRequestInfo);
            }
        }
    }
    GetResponse getResponse = null;
    if (!partitionRequestInfoList.isEmpty()) {
        GetRequest getRequest = new GetRequest(correlationIdGenerator.incrementAndGet(), "replication-fetch-" + dataNodeId.getHostname(), MessageFormatFlags.All, partitionRequestInfoList, GetOption.None);
        long startTime = SystemTime.getInstance().milliseconds();
        try {
            connectedChannel.send(getRequest);
            ChannelOutput channelOutput = connectedChannel.receive();
            getResponse = GetResponse.readFrom(new DataInputStream(channelOutput.getInputStream()), clusterMap);
            long getRequestTime = SystemTime.getInstance().milliseconds() - startTime;
            replicationMetrics.updateGetRequestTime(getRequestTime, replicatingFromRemoteColo, replicatingOverSsl, datacenterName);
            if (getResponse.getError() != ServerErrorCode.No_Error) {
                logger.error("Remote node: " + remoteNode + " Thread name: " + threadName + " Remote replicas: " + replicasToReplicatePerNode + " GetResponse from replication: " + getResponse.getError());
                throw new ReplicationException(" Get Request returned error when trying to get missing keys " + getResponse.getError());
            }
        } catch (IOException e) {
            responseHandler.onEvent(replicasToReplicatePerNode.get(0).getReplicaId(), e);
            throw e;
        }
    }
    return getResponse;
}
Also used : ChannelOutput(com.github.ambry.network.ChannelOutput) ArrayList(java.util.ArrayList) IOException(java.io.IOException) PartitionRequestInfo(com.github.ambry.protocol.PartitionRequestInfo) DataInputStream(java.io.DataInputStream) StoreKey(com.github.ambry.store.StoreKey) GetResponse(com.github.ambry.protocol.GetResponse) GetRequest(com.github.ambry.protocol.GetRequest) BlobId(com.github.ambry.commons.BlobId)

Example 2 with ChannelOutput

use of com.github.ambry.network.ChannelOutput in project ambry by linkedin.

the class ReplicaThread method getMessagesForMissingKeys.

/**
 * Gets the messages for the keys that are missing from the local store by issuing a {@link GetRequest} to the remote
 * node, if there are any missing keys. If there are no missing keys to be fetched, then no request is issued and a
 * null response is returned.
 * @param connectedChannel The connection channel to the remote node
 * @param exchangeMetadataResponseList The list of metadata response from the remote node
 * @param replicasToReplicatePerNode The list of remote replicas for the remote node
 * @param remoteNode The remote node from which replication needs to happen
 * @param remoteColoGetRequestForStandby boolean which indicates if we are getting missing keys for standby or
 *                                       non-leader replica pairs during leader-based replication.
 * @return The response that contains the missing messages; or null if no request was issued because there were no
 * keys missing.
 * @throws ReplicationException
 * @throws IOException
 */
private GetResponse getMessagesForMissingKeys(ConnectedChannel connectedChannel, List<ExchangeMetadataResponse> exchangeMetadataResponseList, List<RemoteReplicaInfo> replicasToReplicatePerNode, DataNodeId remoteNode, boolean remoteColoGetRequestForStandby) throws ReplicationException, IOException {
    List<PartitionRequestInfo> partitionRequestInfoList = new ArrayList<PartitionRequestInfo>();
    for (int i = 0; i < exchangeMetadataResponseList.size(); i++) {
        ExchangeMetadataResponse exchangeMetadataResponse = exchangeMetadataResponseList.get(i);
        RemoteReplicaInfo remoteReplicaInfo = replicasToReplicatePerNode.get(i);
        if (exchangeMetadataResponse.serverErrorCode == ServerErrorCode.No_Error) {
            Set<StoreKey> missingStoreKeys = exchangeMetadataResponse.getMissingStoreKeys();
            if (missingStoreKeys.size() > 0) {
                if (remoteNode instanceof CloudDataNode) {
                    logger.trace("Replicating blobs from CloudDataNode: {}", missingStoreKeys);
                }
                ArrayList<BlobId> keysToFetch = new ArrayList<BlobId>();
                for (StoreKey storeKey : missingStoreKeys) {
                    keysToFetch.add((BlobId) storeKey);
                }
                PartitionRequestInfo partitionRequestInfo = new PartitionRequestInfo(remoteReplicaInfo.getReplicaId().getPartitionId(), keysToFetch);
                partitionRequestInfoList.add(partitionRequestInfo);
            }
        }
    }
    GetResponse getResponse = null;
    if (!partitionRequestInfoList.isEmpty()) {
        GetRequest getRequest = new GetRequest(correlationIdGenerator.incrementAndGet(), GetRequest.Replication_Client_Id_Prefix + dataNodeId.getHostname() + "[" + dataNodeId.getDatacenterName() + "]", MessageFormatFlags.All, partitionRequestInfoList, replicationConfig.replicationIncludeAll ? GetOption.Include_All : GetOption.None);
        long startTime = time.milliseconds();
        try {
            ChannelOutput channelOutput = connectedChannel.sendAndReceive(getRequest);
            getResponse = GetResponse.readFrom(channelOutput.getInputStream(), clusterMap);
            long getRequestTime = time.milliseconds() - startTime;
            replicationMetrics.updateGetRequestTime(getRequestTime, replicatingFromRemoteColo, replicatingOverSsl, datacenterName, remoteColoGetRequestForStandby);
            if (getResponse.getError() != ServerErrorCode.No_Error) {
                logger.error("Remote node: {} Thread name: {} Remote replicas: {} GetResponse from replication: {}", remoteNode, threadName, replicasToReplicatePerNode, getResponse.getError());
                throw new ReplicationException(" Get Request returned error when trying to get missing keys " + getResponse.getError());
            }
        } catch (IOException e) {
            responseHandler.onEvent(replicasToReplicatePerNode.get(0).getReplicaId(), e);
            throw e;
        }
    }
    return getResponse;
}
Also used : ChannelOutput(com.github.ambry.network.ChannelOutput) ArrayList(java.util.ArrayList) IOException(java.io.IOException) PartitionRequestInfo(com.github.ambry.protocol.PartitionRequestInfo) StoreKey(com.github.ambry.store.StoreKey) CloudDataNode(com.github.ambry.clustermap.CloudDataNode) GetResponse(com.github.ambry.protocol.GetResponse) GetRequest(com.github.ambry.protocol.GetRequest) BlobId(com.github.ambry.commons.BlobId)

Example 3 with ChannelOutput

use of com.github.ambry.network.ChannelOutput in project ambry by linkedin.

the class Http2BlockingChannel method sendAndReceive.

public ChannelOutput sendAndReceive(Send request) throws IOException {
    Channel streamChannel;
    try {
        streamChannel = channelPool.acquire().get(http2ClientConfig.http2BlockingChannelAcquireTimeoutMs, TimeUnit.MILLISECONDS);
    } catch (Exception e) {
        throw new IOException("Can't acquire stream channel from " + getRemoteHost() + ":" + getRemotePort(), e);
    }
    CompletableFuture<ByteBuf> responsePromise = new CompletableFuture<ByteBuf>();
    streamChannel.attr(RESPONSE_PROMISE).set(responsePromise);
    streamChannel.attr(CHANNEL_POOL_ATTRIBUTE_KEY).set(channelPool);
    boolean success = streamChannel.writeAndFlush(request).awaitUninterruptibly(http2ClientConfig.http2BlockingChannelSendTimeoutMs, TimeUnit.MILLISECONDS);
    if (!success) {
        if (streamChannel.attr(RESPONSE_PROMISE).getAndSet(null) != null) {
            channelPool.release(streamChannel);
        }
        throw new IOException("Failed to write and flush request on time, from " + getRemoteHost() + ":" + getRemotePort());
    }
    ByteBuf responseByteBuf;
    try {
        responseByteBuf = responsePromise.get(http2ClientConfig.http2BlockingChannelReceiveTimeoutMs, TimeUnit.MILLISECONDS);
    } catch (InterruptedException | ExecutionException | TimeoutException e) {
        if (streamChannel.attr(RESPONSE_PROMISE).getAndSet(null) != null) {
            channelPool.release(streamChannel);
        }
        throw new IOException("Failed to receive response from " + getRemoteHost() + ":" + getRemotePort(), e);
    }
    NettyByteBufDataInputStream dataInputStream = new NettyByteBufDataInputStream(responseByteBuf);
    // the size of remaining data should be dataInputStream.readLong() - 8
    return new ChannelOutput(dataInputStream, dataInputStream.readLong() - Long.BYTES);
}
Also used : NettyByteBufDataInputStream(com.github.ambry.utils.NettyByteBufDataInputStream) ChannelOutput(com.github.ambry.network.ChannelOutput) ConnectedChannel(com.github.ambry.network.ConnectedChannel) Channel(io.netty.channel.Channel) IOException(java.io.IOException) ByteBuf(io.netty.buffer.ByteBuf) TimeoutException(java.util.concurrent.TimeoutException) IOException(java.io.IOException) ExecutionException(java.util.concurrent.ExecutionException) GeneralSecurityException(java.security.GeneralSecurityException) CompletableFuture(java.util.concurrent.CompletableFuture) ExecutionException(java.util.concurrent.ExecutionException) TimeoutException(java.util.concurrent.TimeoutException)

Example 4 with ChannelOutput

use of com.github.ambry.network.ChannelOutput in project ambry by linkedin.

the class EmbeddedChannelPool method testReceiveTimeout.

/**
 * Test when receiving response times out
 * @throws Exception
 */
@Test
public void testReceiveTimeout() throws Exception {
    CountDownLatch latch = new CountDownLatch(1);
    Http2BlockingChannelResponseHandler handler = new Http2BlockingChannelResponseHandler() {

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception {
            latch.await();
            super.channelRead0(ctx, msg);
        }
    };
    long receiveTimeoutMs = 1000;
    EmbeddedChannelPool channelPool = new EmbeddedChannelPool(handler);
    InetSocketAddress address = new InetSocketAddress("localhost", 2021);
    Http2BlockingChannel blockingChannel = new Http2BlockingChannel(channelPool, address, createHttp2ClientConfig(1000, 1000, receiveTimeoutMs));
    int sendSize = 1234;
    byte[] byteArray = new byte[sendSize];
    new Random().nextBytes(byteArray);
    ByteBuf content = PooledByteBufAllocator.DEFAULT.heapBuffer(sendSize).writeBytes(byteArray);
    MockSend send = new MockSend(content);
    ScheduledFuture<ChannelOutput> future = service.schedule(() -> blockingChannel.sendAndReceive(send), 0, TimeUnit.MILLISECONDS);
    EmbeddedChannel channel = channelPool.getCurrentChannel();
    int responseSize = 100;
    byteArray = new byte[responseSize];
    new Random().nextBytes(byteArray);
    ByteBuf response = PooledByteBufAllocator.DEFAULT.heapBuffer(responseSize + 8).writeLong(responseSize + 8).writeBytes(byteArray);
    service.submit(() -> {
        channel.writeInbound(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, response));
    });
    // Blocking channel is waiting for response. Response is blocked by countdown latch.
    Thread.sleep(2 * receiveTimeoutMs);
    latch.countDown();
    try {
        future.get();
        Assert.fail("Should fail on receiving response");
    } catch (Exception e) {
    }
    content.release();
// response.release();
}
Also used : DefaultFullHttpResponse(io.netty.handler.codec.http.DefaultFullHttpResponse) ChannelOutput(com.github.ambry.network.ChannelOutput) InetSocketAddress(java.net.InetSocketAddress) EmbeddedChannel(io.netty.channel.embedded.EmbeddedChannel) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) CountDownLatch(java.util.concurrent.CountDownLatch) ByteBuf(io.netty.buffer.ByteBuf) TimeoutException(java.util.concurrent.TimeoutException) IOException(java.io.IOException) Random(java.util.Random) FullHttpResponse(io.netty.handler.codec.http.FullHttpResponse) DefaultFullHttpResponse(io.netty.handler.codec.http.DefaultFullHttpResponse) Test(org.junit.Test)

Example 5 with ChannelOutput

use of com.github.ambry.network.ChannelOutput in project ambry by linkedin.

the class EmbeddedChannelPool method testSendAndReceive.

/**
 * Test for the basic functions for Http2BlockingChannel, sendAndReceive
 * @throws Exception
 */
@Test
@Ignore
public void testSendAndReceive() throws Exception {
    EmbeddedChannelPool channelPool = new EmbeddedChannelPool();
    InetSocketAddress address = new InetSocketAddress("localhost", 2021);
    Http2BlockingChannel blockingChannel = new Http2BlockingChannel(channelPool, address, createHttp2ClientConfig());
    int sendSize = 1234;
    byte[] byteArray = new byte[sendSize];
    new Random().nextBytes(byteArray);
    ByteBuf content = PooledByteBufAllocator.DEFAULT.heapBuffer(sendSize).writeBytes(byteArray);
    MockSend send = new MockSend(content);
    ScheduledFuture<ChannelOutput> future = service.schedule(() -> blockingChannel.sendAndReceive(send), 0, TimeUnit.MILLISECONDS);
    EmbeddedChannel channel = channelPool.getCurrentChannel();
    int responseSize = 100;
    byteArray = new byte[responseSize];
    new Random().nextBytes(byteArray);
    ByteBuf response = PooledByteBufAllocator.DEFAULT.heapBuffer(responseSize + 8).writeLong(responseSize + 8).writeBytes(byteArray);
    channel.writeInbound(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, response));
    ChannelOutput output = future.get();
    int streamSize = (int) output.getStreamSize();
    Assert.assertEquals(responseSize, streamSize);
    byte[] obtainedResponse = new byte[streamSize];
    output.getInputStream().read(obtainedResponse);
    Assert.assertArrayEquals(byteArray, obtainedResponse);
    // Make sure the channel is released
    Assert.assertNull(channelPool.getCurrentChannelNow());
    content.release();
    response.release();
}
Also used : DefaultFullHttpResponse(io.netty.handler.codec.http.DefaultFullHttpResponse) Random(java.util.Random) ChannelOutput(com.github.ambry.network.ChannelOutput) InetSocketAddress(java.net.InetSocketAddress) EmbeddedChannel(io.netty.channel.embedded.EmbeddedChannel) ByteBuf(io.netty.buffer.ByteBuf) Ignore(org.junit.Ignore) Test(org.junit.Test)

Aggregations

ChannelOutput (com.github.ambry.network.ChannelOutput)6 IOException (java.io.IOException)5 ByteBuf (io.netty.buffer.ByteBuf)3 ArrayList (java.util.ArrayList)3 BlobId (com.github.ambry.commons.BlobId)2 GetRequest (com.github.ambry.protocol.GetRequest)2 GetResponse (com.github.ambry.protocol.GetResponse)2 PartitionRequestInfo (com.github.ambry.protocol.PartitionRequestInfo)2 StoreKey (com.github.ambry.store.StoreKey)2 NettyByteBufDataInputStream (com.github.ambry.utils.NettyByteBufDataInputStream)2 EmbeddedChannel (io.netty.channel.embedded.EmbeddedChannel)2 DefaultFullHttpResponse (io.netty.handler.codec.http.DefaultFullHttpResponse)2 InetSocketAddress (java.net.InetSocketAddress)2 Random (java.util.Random)2 TimeoutException (java.util.concurrent.TimeoutException)2 Test (org.junit.Test)2 CloudDataNode (com.github.ambry.clustermap.CloudDataNode)1 ConnectedChannel (com.github.ambry.network.ConnectedChannel)1 ReplicaMetadataRequest (com.github.ambry.protocol.ReplicaMetadataRequest)1 ReplicaMetadataRequestInfo (com.github.ambry.protocol.ReplicaMetadataRequestInfo)1