[ISSUE #10696] Fix async request future cleanup on synchronous send failure - #10697
[ISSUE #10696] Fix async request future cleanup on synchronous send failure#10697ai-yang wants to merge 1 commit into
Conversation
RockteMQ-AI
left a comment
There was a problem hiding this comment.
Review by github-manager-bot
Summary
This PR fixes a resource leak in the async request-reply path of DefaultMQProducerImpl. When sendDefaultImpl, sendSelectImpl, or sendKernelImpl throws synchronously (before async handoff), the registered RequestResponseFuture remains in RequestFutureHolder.requestFutureTable until timeout. The fix wraps each send invocation in a try/finally block with a sendInvocationCompleted flag, removing the future only when the send call itself fails.
Findings
-
[Info]
DefaultMQProducerImpl.java:1662-1689— The try/finally + boolean flag pattern is clean and idiomatic. UsingConcurrentHashMap.remove(key, value)(conditional remove) correctly avoids clobbering a concurrent replacement under the same correlation ID. Good defensive design. -
[Info]
DefaultMQProducerImpl.java— All threerequest()overloads (default, selector, kernel/queue) apply the same pattern consistently. This uniformity makes the fix easy to verify and maintain. -
[Info]
DefaultMQProducerTest.java:483-508— The test rename fromtestAsyncRequest_OnExceptiontotestAsyncRequest_SynchronousExceptionRemovesFuturebetter reflects the actual behavior being tested. The assertion change from checking callback invocation to checking future removal is semantically correct for this scenario. -
[Info]
selector/DefaultMQProducerImplTest.java:134-175— Good coverage of all three send paths (default, selector, queue) with dedicated regression tests. The@Before/@Aftercleanup ofrequestFutureTableprevents test pollution. -
[Info] The
timeout - costcomputation is a pre-existing concern (could go negative ifcost > timeout), but is out of scope for this PR.
Verdict
Well-structured, focused fix for a real resource leak. The code is clean, tests are thorough (995 tests passing), and the conditional remove(key, value) shows attention to concurrent correctness. No issues found.
Automated review by github-manager-bot
RockteMQ-AI
left a comment
There was a problem hiding this comment.
Summary
Defensive fix with proper validation and test coverage. LGTM.
Automated review by github-manager-bot
d7d9905 to
56a47e8
Compare
|
Updated and ready for another review round:
The background name-server lookup warnings in the test log are expected in the isolated test environment and did not affect the result. @drpmma @RongtongJin, could you please take a look when convenient? |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #10697 +/- ##
=============================================
- Coverage 49.39% 49.33% -0.07%
+ Complexity 14237 14222 -15
=============================================
Files 1390 1390
Lines 103123 103138 +15
Branches 13484 13487 +3
=============================================
- Hits 50940 50878 -62
- Misses 46031 46087 +56
- Partials 6152 6173 +21 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…send failure Signed-off-by: Rui <1685901819@qq.com>
56a47e8 to
e340eb4
Compare
|
Refreshed onto Resolved the import conflict in Clean JDK 8 verification: @drpmma @RongtongJin, could you take a human review of the synchronous-send-failure cleanup when convenient? The updated commit also triggers a new full CI run. |
RockteMQ-AI
left a comment
There was a problem hiding this comment.
Summary
Re-reviewed after new commits. The try-finally pattern correctly cleans up RequestFutureHolder entries when sendDefaultImpl/sendSelectImpl/sendKernelImpl throws synchronously (e.g., serialization failure, no available broker).
Key points:
sendInvocationCompletedflag is a clean way to detect synchronous failure before the async callback takes overremove(correlationId, requestResponseFuture)with value check prevents removing a different future if the ID was somehow reused- Applied consistently across all three
request()overloads - Tests verify cleanup on sync failure and normal async path
LGTM — re-approved with the latest changes.
Automated review by github-manager-bot
Which Issue(s) This PR Fixes
Brief Description
The asynchronous request-reply overloads register a
RequestResponseFuturebefore invoking the underlying send method. When send initiation throws synchronously, the exception reaches the caller but the future remains inRequestFutureHolderuntil timeout processing, which retains request state and can deliver a callback after the synchronous failure.This change:
sendDefaultImpl,sendSelectImpl, orsendKernelImpldoes not return normally;remove(correlationId, requestResponseFuture)so a concurrent replacement under the same key is not removed;How Did You Test This Change?
Verified on 2026-09-11 at head
e340eb4d4b1372d04c9cdf3c57799a6c9724662e, rebased ontodevelopat1a50c6e4e35524ac22055b76fc0ff2a6d3cff99f, using JDK 8 and Maven 3.8.1:mvn -pl client -am -DskipITs \ -Dtest=DefaultMQProducerTest,DefaultMQProducerImplTest \ -Dsurefire.failIfNoSpecifiedTests=false clean testDefaultMQProducerTest: 41 tests passed.DefaultMQProducerImplTest: 37 tests passed.git diff --check: passed.The new regression assertion was previously verified to fail before the production fix because the correlation ID remained in
requestFutureTable. Earlier full-client reactor evidence (995 tests, 0 failures/errors, 1 skip) predates this rebase; the latest local run above is targeted, and the full CI matrix has been triggered for the refreshed head.