-
Notifications
You must be signed in to change notification settings - Fork 444
RATIS-1240. Add input stream to DataStreamApi for read operations in Server #1469
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
282a469
a106ecf
44c26cc
55cf4e5
ad8cbe2
a6b4b15
b672155
d9b4431
611b62a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,6 +33,7 @@ | |
| import org.apache.ratis.proto.RaftProtos.CommitInfoProto; | ||
| import org.apache.ratis.proto.RaftProtos.DataStreamPacketHeaderProto.Type; | ||
| import org.apache.ratis.proto.RaftProtos.RaftClientRequestProto; | ||
| import org.apache.ratis.proto.RaftProtos.RaftClientRequestProto.TypeCase; | ||
| import org.apache.ratis.protocol.ClientId; | ||
| import org.apache.ratis.protocol.ClientInvocationId; | ||
| import org.apache.ratis.protocol.DataStreamReply; | ||
|
|
@@ -42,6 +43,7 @@ | |
| import org.apache.ratis.protocol.RaftPeer; | ||
| import org.apache.ratis.protocol.RaftPeerId; | ||
| import org.apache.ratis.protocol.RoutingTable; | ||
| import org.apache.ratis.protocol.exceptions.AlreadyClosedException; | ||
| import org.apache.ratis.protocol.exceptions.AlreadyExistsException; | ||
| import org.apache.ratis.protocol.exceptions.DataStreamException; | ||
| import org.apache.ratis.server.RaftConfiguration; | ||
|
|
@@ -53,6 +55,7 @@ | |
| import org.apache.ratis.statemachine.StateMachine.DataChannel; | ||
| import org.apache.ratis.thirdparty.com.google.common.annotations.VisibleForTesting; | ||
| import org.apache.ratis.thirdparty.io.netty.buffer.ByteBuf; | ||
| import org.apache.ratis.thirdparty.io.netty.channel.ChannelFuture; | ||
| import org.apache.ratis.thirdparty.io.netty.channel.ChannelHandlerContext; | ||
| import org.apache.ratis.thirdparty.io.netty.channel.ChannelId; | ||
| import org.apache.ratis.util.ConcurrentUtils; | ||
|
|
@@ -68,6 +71,7 @@ | |
| import org.slf4j.LoggerFactory; | ||
|
|
||
| import java.io.IOException; | ||
| import java.io.InterruptedIOException; | ||
| import java.nio.ByteBuffer; | ||
| import java.util.Collection; | ||
| import java.util.Collections; | ||
|
|
@@ -352,9 +356,36 @@ static DataStreamReplyByteBuffer newDataStreamReplyByteBuffer(DataStreamRequestB | |
| .setDataStreamPacket(request) | ||
| .setBuffer(buffer) | ||
| .setSuccess(reply.isSuccess()) | ||
| .setCommitInfos(reply.getCommitInfos()) | ||
| .build(); | ||
| } | ||
|
|
||
| static DataStreamReplyByteBuffer newDataStreamReadOnlyReplyByteBuffer(DataStreamRequestByteBuf request, | ||
| long streamOffset, ByteBuffer buffer) { | ||
| final ByteBuffer readOnlyBuffer = buffer.asReadOnlyBuffer(); | ||
| return DataStreamReplyByteBuffer.newBuilder() | ||
| .setClientId(request.getClientId()) | ||
| .setType(Type.STREAM_DATA) | ||
| .setStreamId(request.getStreamId()) | ||
| .setStreamOffset(streamOffset) | ||
| .setBuffer(readOnlyBuffer) | ||
| .setSuccess(true) | ||
| .setBytesWritten(readOnlyBuffer.remaining()) | ||
| .build(); | ||
| } | ||
|
|
||
| private static CompletableFuture<Void> writeAndFlush(ChannelHandlerContext ctx, DataStreamReply reply) { | ||
| final CompletableFuture<Void> future = new CompletableFuture<>(); | ||
| ctx.writeAndFlush(reply).addListener(channelFuture -> { | ||
| if (channelFuture.isSuccess()) { | ||
| future.complete(null); | ||
| } else { | ||
| future.completeExceptionally(channelFuture.cause()); | ||
| } | ||
| }); | ||
| return future; | ||
| } | ||
|
peterxcli marked this conversation as resolved.
Outdated
|
||
|
|
||
| private void sendReply(List<CompletableFuture<DataStreamReply>> remoteWrites, | ||
| DataStreamRequestByteBuf request, long bytesWritten, Collection<CommitInfoProto> commitInfos, | ||
| ChannelHandlerContext ctx) { | ||
|
|
@@ -450,6 +481,23 @@ private void readImpl(DataStreamRequestByteBuf request, ChannelHandlerContext ct | |
| // add to ChannelMap | ||
| channels.add(channelId, key); | ||
|
|
||
| if (request.getType() == Type.STREAM_HEADER) { | ||
| final RaftClientRequest raftClientRequest = toRaftClientRequest(request); | ||
| if (raftClientRequest.is(TypeCase.READ)) { | ||
| submitReadOnlyRequest(request, raftClientRequest, ctx).whenComplete((v, exception) -> { | ||
| try { | ||
| if (exception != null) { | ||
| replyDataStreamException(server, exception, raftClientRequest, request, ctx); | ||
| } | ||
| } finally { | ||
| request.release(); | ||
| channels.remove(channelId, key); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
| }); | ||
| return; | ||
| } | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The new read streams have nothing to do with the existing write streams. Let's do the check in NettyServerStreamRpc. @@ -235,6 +237,9 @@ public class NettyServerStreamRpc implements DataStreamServerRpc {
final DataStreamRequestByteBuf request = (DataStreamRequestByteBuf)msg;
try(UncheckedAutoCloseable autoReset = requestRef.set(request)) {
+ if (reads.process(request, ctx)) {
+ return;
+ }
requests.read(request, ctx, proxies.get(request)::getDataStreamOutput);
}
} |
||
|
|
||
| final StreamInfo info; | ||
| if (request.getType() == Type.STREAM_HEADER) { | ||
| final MemoizedSupplier<StreamInfo> supplier = JavaUtils.memoize( | ||
|
|
@@ -510,6 +558,72 @@ private void readImpl(DataStreamRequestByteBuf request, ChannelHandlerContext ct | |
| }); | ||
| } | ||
|
|
||
| private static RaftClientRequest toRaftClientRequest(DataStreamRequestByteBuf request) { | ||
| try { | ||
| return ClientProtoUtils.toRaftClientRequest(RaftClientRequestProto.parseFrom(request.slice().nioBuffer())); | ||
| } catch (Throwable e) { | ||
| throw new CompletionException(e); | ||
| } | ||
| } | ||
|
|
||
| private CompletableFuture<Void> submitReadOnlyRequest(DataStreamRequestByteBuf request, | ||
| RaftClientRequest raftClientRequest, ChannelHandlerContext ctx) { | ||
| try { | ||
| final StateMachine.DataChannel readOnlyDataStream = new StateMachine.DataChannel() { | ||
| private long streamOffset; | ||
| private boolean closed; | ||
|
|
||
| @Override | ||
| public synchronized boolean isOpen() { | ||
| return !closed; | ||
| } | ||
|
|
||
| @Override | ||
| public synchronized void close() { | ||
| closed = true; | ||
| } | ||
|
|
||
| @Override | ||
| public synchronized void force(boolean metadata) throws IOException { | ||
| if (!isOpen()) { | ||
| throw new AlreadyClosedException("Channel closed at offset " + streamOffset); | ||
| } | ||
| ctx.flush(); | ||
| } | ||
|
|
||
| @Override | ||
| public synchronized int write(ByteBuffer buffer) throws IOException { | ||
| if (!isOpen()) { | ||
| throw new AlreadyClosedException("Channel closed at offset " + streamOffset); | ||
| } | ||
| final int length = buffer.remaining(); | ||
| final DataStreamReplyByteBuffer reply = newDataStreamReadOnlyReplyByteBuffer(request, streamOffset, buffer); | ||
| final ChannelFuture future = ctx.writeAndFlush(reply); | ||
| try { | ||
| future.await(); | ||
| } catch (InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| throw new InterruptedIOException( | ||
| "Interrupted while writing " + length + " bytes at offset " + streamOffset); | ||
| } | ||
| if (!future.isSuccess()) { | ||
| final Throwable cause = future.cause(); | ||
| if (cause instanceof IOException) { | ||
| throw (IOException) cause; | ||
| } | ||
|
peterxcli marked this conversation as resolved.
Outdated
|
||
| throw new IOException("Failed to write " + length + " bytes at offset " + streamOffset, cause); | ||
| } | ||
| streamOffset += length; | ||
| return length; | ||
| } | ||
| }; | ||
| return server.streamReadOnlyAsync(raftClientRequest, readOnlyDataStream) | ||
| .thenCompose(reply -> writeAndFlush(ctx, newDataStreamReplyByteBuffer(request, reply))); | ||
| } catch (IOException e) { | ||
| return JavaUtils.completeExceptionally(e); | ||
| } | ||
| } | ||
|
|
||
| static void assertReplyCorrespondingToRequest( | ||
| final DataStreamRequestByteBuf request, final DataStreamReply reply) { | ||
| Preconditions.assertTrue(request.getClientId().equals(reply.getClientId())); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,6 +24,7 @@ | |
| import java.util.Collection; | ||
| import java.util.Objects; | ||
| import java.util.Optional; | ||
| import java.util.concurrent.CompletableFuture; | ||
| import org.apache.ratis.conf.Parameters; | ||
| import org.apache.ratis.conf.RaftProperties; | ||
| import org.apache.ratis.proto.RaftProtos.CommitInfoProto; | ||
|
|
@@ -32,6 +33,8 @@ | |
| import org.apache.ratis.protocol.AdminProtocol; | ||
| import org.apache.ratis.protocol.RaftClientAsynchronousProtocol; | ||
| import org.apache.ratis.protocol.RaftClientProtocol; | ||
| import org.apache.ratis.protocol.RaftClientReply; | ||
| import org.apache.ratis.protocol.RaftClientRequest; | ||
| import org.apache.ratis.protocol.RaftGroup; | ||
| import org.apache.ratis.protocol.RaftGroupId; | ||
| import org.apache.ratis.protocol.RaftGroupMemberId; | ||
|
|
@@ -150,6 +153,18 @@ default RaftGroup getGroup() { | |
| /** @return the data stream rpc service. */ | ||
| DataStreamServerRpc getDataStreamServerRpc(); | ||
|
|
||
| /** | ||
| * Submit a read-only request whose response may be streamed through the data stream RPC. | ||
| * | ||
| * @param request the read-only request | ||
| * @param stream the stream for response data chunks | ||
| * @return a future for the terminal reply | ||
| */ | ||
| default CompletableFuture<RaftClientReply> streamReadOnlyAsync( | ||
| RaftClientRequest request, StateMachine.DataChannel stream) throws IOException { | ||
| throw new UnsupportedOperationException("This method is NOT supported."); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This new method seems not needed since we may:
Of course, we should start with Phase 1 for simpilcity. |
||
|
|
||
| /** @return the {@link RpcType}. */ | ||
| default RpcType getRpcType() { | ||
| return getFactory().getRpcType(); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.