RATIS-2546. Add input stream to DataStreamApi for read operations in Client#1481
Conversation
Signed-off-by: peterxcli <peterxcli@gmail.com>
Signed-off-by: peterxcli <peterxcli@gmail.com>
Signed-off-by: peterxcli <peterxcli@gmail.com>
|
cc @szetszwo |
|
The no-MD5 sequential reads comparison:
The no-MD5 random reads comparison:
I'm looking into why larger buffer would cause throughput to degrade. |
There was a problem hiding this comment.
@peterxcli , thanks a lot for working on this!
The PR is quite big. Let's separate the DataStreamReply change first. Please see the comments inlined and also https://issues.apache.org/jira/secure/attachment/13082807/1481_review_DataStreamReply.patch (updated the link in the next comment.)
Signed-off-by: peterxcli <peterxcli@gmail.com>
szetszwo
left a comment
There was a problem hiding this comment.
@peterxcli , thanks for updating this!
Let's have one more simple change to add an executor and terminalReply (RATIS-2564). Please see the comments inlined and also https://issues.apache.org/jira/secure/attachment/13082846/1481_review3_refactoring.patch
Signed-off-by: peterxcli <peterxcli@gmail.com>
|
|
||
| /** Async call to send a request and receive multiple replies for the request. */ | ||
| default CompletableFuture<DataStreamReply> streamAsync( | ||
| DataStreamRequest request, Consumer<DataStreamReply> replyConsumer) { |
There was a problem hiding this comment.
@peterxcli , Let's borrow the idea from gRPC StreamObserver and add a similar interface. It is more flexible for future changes.
//ratis-common
package org.apache.ratis.datastream;
/** An interface similar to gRPC {@link org.apache.ratis.thirdparty.io.grpc.stub.StreamObserver}. */
public interface DataStreamObserver<V> {
void onNext(V value);
// see if onError(Throwable) and onCompleted() are useful. Or we may add them later.
}Then our interface will be similar to gRPC service https://github.com/apache/ratis/blob/master/ratis-grpc/src/main/java/org/apache/ratis/grpc/server/GrpcServerProtocolClient.java#L174-L181
//DataStreamClientRpc
/** Async call to send a request and receive multiple replies for the request. */
default CompletableFuture<DataStreamReply> streamAsync(DataStreamRequest request,
DataStreamObserver<DataStreamReplyByteBuf> replyHandler) {
throw new UnsupportedOperationException(getClass() + " does not support "
+ JavaUtils.getCurrentStackTraceElement().getMethodName());
}Signed-off-by: peterxcli <peterxcli@gmail.com>
Signed-off-by: peterxcli <peterxcli@gmail.com>
|
@szetszwo thanks for the grpc observer suggestion, just added a initial version of it. I also add onError and onComplete interface method for the observer, because we can decide which hook to call base on the terminal reply. Please take a look, Thanks! |
Co-Authored-By: amaliujia <amaliujia@apache.org> Signed-off-by: peterxcli <peterxcli@gmail.com>
aa3ab09 to
7b15a13
Compare
Signed-off-by: peterxcli <peterxcli@gmail.com>
szetszwo
left a comment
There was a problem hiding this comment.
@peterxcli , thanks for the update!
In DataStreamInput, readAsync() should return a ReferenceCountedObject so the user can retain/release the buffer.
Please see the comments inlined and also https://issues.apache.org/jira/secure/attachment/13082885/1481_review4.patch
Co-Authored-By: szetszwo <szetszwo@apache.org> Signed-off-by: peterxcli <peterxcli@gmail.com>
szetszwo
left a comment
There was a problem hiding this comment.
@peterxcli , thanks for the update!
- Found that all the checkstyle exclude and findbugs exclude are not needed. Please revert them.
- See the comments inlined and also https://issues.apache.org/jira/secure/attachment/13082908/1481_review5.patch
| reply.retain(); | ||
| for (CompletableFuture<ReferenceCountedObject<DataStreamReply>> pending; | ||
| (pending = pendingReads.poll()) != null; ) { | ||
| if (pending.complete(reply)) { | ||
| return; | ||
| } | ||
| } | ||
| replies.add(reply); |
There was a problem hiding this comment.
Since all entries in pendingReads must not be completed, remove the loop.
reply.retain();
final CompletableFuture<ReferenceCountedObject<DataStreamReply>> pending = pendingReads.poll();
if (pending != null) {
final boolean completed = pending.complete(reply);
Preconditions.assertTrue(completed);
return;
}
replies.add(reply);There was a problem hiding this comment.
But if the onNext API caller cancel the one of the future, the other future might never get the reply
Related testcase added in this patch:
And since you assertion for that case, should we remove the above testcase and add document and tell user DONT CANCEL the return future?
There was a problem hiding this comment.
I removed the testReceiveSkipsCancelledPendingRead first, if you think we should change the api behaviour, feel free to ask me to add it back. Thanks!
| .setReleaseMethod(r -> { | ||
| if (r != null) { | ||
| Preconditions.assertSame(reply, r, "reply"); | ||
| reply.release(); | ||
| } | ||
| }).build(); |
There was a problem hiding this comment.
Let's change Preconditions.assertSame to return DataStreamReplyByteBuf:
final ReferenceCountedObject<DataStreamReply> ref = ReferenceCountedObject.<DataStreamReply>newBuilder()
.setValue((DataStreamReplyByteBuf) msg)
.setReleaseMethod(r -> Preconditions.assertSame(reply, r, "reply").release())
.build();+++ b/ratis-common/src/main/java/org/apache/ratis/util/Preconditions.java
@@ -88,9 +88,10 @@ public interface Preconditions {
() -> name + ": expected == " + expected + " but computed == " + computed);
}
- static void assertSame(Object expected, Object computed, String name) {
+ static <T> T assertSame(T expected, Object computed, String name) {
assertTrue(expected == computed,
() -> name + ": expected == " + expected + " but computed == " + computed);
+ return expected;
} There was a problem hiding this comment.
Oops, this actually is not working. Need to keep check not null first.
.setReleaseMethod(r -> {
if (r != null) {
Preconditions.assertSame(reply, r, "reply");
reply.release();
}
}).build();There was a problem hiding this comment.
Thanks, I plan to add as static RC builder function for DataStreamReply:
public static ReferenceCountedObject<DataStreamReply> asReferenceCounted(DataStreamReplyByteBuf reply) {
Objects.requireNonNull(reply, "reply == null");
return ReferenceCountedObject.<DataStreamReply>newBuilder()
.setValue(reply)
.setReleaseMethod(r -> {
if (r != null) {
Preconditions.assertSame(reply, r, "reply").release();
}
})
.build();
}so prod and test can shared the same RC-DataStreamReply constructor code
|
@szetszwo found a new class |
Signed-off-by: peterxcli <peterxcli@gmail.com>
Signed-off-by: peterxcli <peterxcli@gmail.com>
Signed-off-by: peterxcli <peterxcli@gmail.com>
|
@szetszwo Thanks for the review again! If you have any idea on further splitting for this PR, please feel free to ask me to do that! |
szetszwo
left a comment
There was a problem hiding this comment.
+1 the change looks good.
|
@peterxcli , thanks a lot for working hard on this! @amaliujia , thanks for reviewing this! |
|
Thanks @szetszwo for continuous review and refactor suggestion! |
What changes were proposed in this pull request?
DataStreamInputinterface and its implementationDataStreamInputImplfor asynchronous, zero-copy read-only data streaming, including thereadAsync()method for consuming replies and proper resource release on close.DataStreamApiinterface and its implementation to providestreamReadOnly()methods for creating read-only streams.DataStreamReplyByteBufclass to support replies backed by NettyByteBuf.streamAsync()in theDataStreamClientRpcinterface that takes a reply consumer, supporting multiple replies per request.What is the link to the Apache JIRA
https://issues.apache.org/jira/browse/RATIS-2546
How was this patch tested?
(Please explain how this patch was tested. Ex: unit tests, manual tests)
(If this patch involves UI changes, please attach a screen-shot; otherwise, remove this)