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;
}
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;
}
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);
}
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();
}
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();
}
Aggregations