Fix Triple gRPC decoder handoff - #16416
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 3.3 #16416 +/- ##
=========================================
Coverage 60.87% 60.88%
+ Complexity 11766 11765 -1
=========================================
Files 1953 1953
Lines 89273 89271 -2
Branches 13473 13473
=========================================
+ Hits 54346 54353 +7
+ Misses 29333 29328 -5
+ Partials 5594 5590 -4
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@zrlw Hello, if you have some time, could you please review this PR for me? Thank you for your time. |
There was a problem hiding this comment.
Pull request overview
This PR fixes Triple gRPC server-side streaming method discovery for no-stub requests by ensuring the lazy method-discovery path reuses the stream’s existing StreamingDecoder so any bytes buffered beyond the first decoded message are preserved across the listener switch.
Changes:
- Reuse
getStreamingDecoder()inLazyFindMethodListenerinstead of creating a temporaryGrpcStreamingDecoder, preserving buffered bytes across handler handoff. - Make the temporary method-discovery fragment listener’s
onClose()a no-op to avoid interfering with the real request stream lifecycle. - Add a regression test that exercises a “full message + partial next frame” layout and verifies decoding continues correctly after switching fragment listeners.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcHttp2ServerTransportListener.java | Ensures lazy method discovery uses the same per-stream decoder and avoids closing the real stream from the temporary discovery listener. |
| dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/h12/grpc/GrpcStreamingDecoderTest.java | Adds coverage for continuing decoding when a fragment listener switch occurs with a buffered partial next frame. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
@EarthChen Hello, if you have some time, could you please review this PR for me? Thank you for your time. |
LI123456mo
left a comment
There was a problem hiding this comment.
Traced this through fully to make sure backpressure/cleanup isn't lost — it isn't, it just moved.
Before this change, streamingDecoder was created fresh inside LazyFindMethodListener() per-call, so closing it there made sense — it was privately owned.
After this change, streamingDecoder is a single instance created once in GenericHttp2ServerTransportListener's constructor and shared with the response side via prepareResponseObserver() → responseObserver.setStreamingDecoder(streamingDecoder). Since it's now shared, this method shouldn't unilaterally close it.
Here's the full chain confirming it's still torn down correctly, every time:
Netty channel physically closes (channel.closeFuture())
→ NettyHttp2ProtocolSelectorHandler: http2TransportListener.close()
→ GenericHttp2ServerTransportListener.close()
→ responseObserver.close() [Http2ServerChannelObserver]
→ streamingDecoder.onStreamClosed()
→ accumulate.close() [CompositeInputStream]
→ each buffered InputStream.close()
→ Netty ByteBuf.release()
So removing getStreamingDecoder().close() from onClose() here isn't dropping cleanup — it was actually a redundant/unsafe extra close on a resource this method no longer exclusively owns (risking a double-close on a shared decoder). The real teardown path is via onStreamClosed(), triggered reliably off Netty's own channel-close event.
|
@RainYuY Hello, I have already deleted it. My initial review shows no impact. Could you please check it? Thank you for your time |
What is the purpose of the change?
Fixes #16414.
This change fixes the gRPC no-stub method discovery path for Triple streaming requests. The lazy method discovery listener now reuses the stream's existing
StreamingDecoderinstead of creating a temporaryGrpcStreamingDecoder, so buffered bytes from a subsequent message are preserved when the listener switches to the resolved business handler.The close callback on the temporary method-discovery listener is also made a no-op because it only exists to resolve the method descriptor and should not close the actual request stream.
A regression test covers a DATA-frame layout where the first decode contains one complete gRPC message followed by the prefix of the next message, then verifies the second message is still decoded after the listener switch.
Checklist
Test: