[ISSUE #10512] Eliminate per-RPC allocation in RemotingCommand (Guava Stopwatch, Constructor copy) and downgrade Netty writability log#10514
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR optimizes remoting command processing by reducing allocation overhead (Stopwatch removal, constructor caching, smaller HashMap defaults) and adds a faster header encoding path while also lowering channel writability log verbosity.
Changes:
- Replace Guava
Stopwatchtiming with aSystem.nanoTime()-based timestamp onRemotingCommand. - Cache
CommandCustomHeaderno-arg constructors and addfastEncodeHeaderAsBuffer. - Reduce per-command allocations (smaller extFields default capacity) and downgrade some writability logs to debug.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| remoting/src/main/java/org/apache/rocketmq/remoting/protocol/statictopic/TopicQueueMappingContext.java | Adds a shared EMPTY instance for “no context” usage. |
| remoting/src/main/java/org/apache/rocketmq/remoting/protocol/RemotingCommand.java | Constructor caching, new fast header encoding method, Stopwatch removal, extFields capacity change. |
| remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyRemotingServer.java | Lowers channel writability logs to debug and guards formatting work. |
| remoting/src/main/java/org/apache/rocketmq/remoting/netty/NettyDecoder.java | Switches decode timing from Stopwatch to nanoTime start timestamp. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
RockteMQ-AI
left a comment
There was a problem hiding this comment.
Review by github-manager-bot
Summary
Eliminates per-RPC allocations in the remoting framework: replaces Guava Stopwatch with System.nanoTime(), caches Constructor lookups, downgrades Netty writability log to DEBUG, and adds TopicQueueMappingContext.EMPTY singleton.
Findings
- [Info]
RemotingCommand.java—HEADER_CTOR_CACHEusesConcurrentHashMap.computeIfAbsent(). The mapping function callsctor.newInstance()which is reflective and could throw. Consider wrapping thecomputeIfAbsentlambda with a try-catch to avoid swallowing checked exceptions silently, or useget()with a null check +putIfAbsent()pattern for clearer error propagation. - [Info]
RemotingCommand.java—processTimerNanosdefaults to 0. IfsetProcessTimerNanos()is never called (e.g., code paths that bypassNettyDecoder),processTimerElapsedMs()returns 0. This is likely intentional but worth a brief comment documenting the contract. - [Info]
NettyRemotingServer.java— Good catch on the log amplification (~900 lines/sec). TheisDebugEnabled()guard is correct and avoids unnecessary string formatting.
Suggestions
- Consider adding a unit test for
newHeaderInstance()to verify the constructor cache works correctly for subclasses with different constructor signatures. - The
EMPTYsingleton inTopicQueueMappingContextis a good optimization. Ensure no code path mutates the singleton (e.g., callsetTopic()on it). If immutability cannot be guaranteed, consider returning a defensive copy or making the fields final.
Overall: Clean micro-optimizations with measurable impact on high-throughput paths. 👍
Automated review by github-manager-bot
cb09268 to
0d43e12
Compare
RockteMQ-AI
left a comment
There was a problem hiding this comment.
Review by github-manager-bot
Summary
Four independent per-RPC micro-optimizations in the remoting framework:
- Guava
Stopwatch→System.nanoTime()(eliminates per-RPC allocation) - Constructor cache for
Class.getDeclaredConstructor()(eliminates ~237 alloc events/60s) - Netty writability log downgrade INFO/WARN → DEBUG (eliminates ~900 lines/sec)
TopicQueueMappingContext.EMPTYsingleton (avoids per-message allocation for non-static-topic messages)
Findings
- [Positive]
RemotingCommand.java— ReplacingStopwatchwith rawlong processTimerNanoseliminates one object allocation per RPC. ThenewHeaderInstance()method withConcurrentHashMapconstructor cache is a clean solution to thegetDeclaredConstructor()defensive copy issue. - [Positive]
NettyRemotingServer.java— DowngradingchannelWritabilityChangedto DEBUG withisDebugEnabled()guard is appropriate. ~900 lines/sec of INFO/WARN is clearly excessive for a writability toggle event. - [Positive]
TopicQueueMappingContext.java— AddingEMPTYsingleton is a simple and effective optimization. - [Warning]
RemotingCommand.java— TheHEADER_CTOR_CACHEusescomputeIfAbsenton aConcurrentHashMap. Under high contention with many distinct header classes, this could cause brief blocking on the bin lock. However, in practice the number of distinct header classes is bounded and small, so this is acceptable. - [Warning]
RemotingCommand.java— TheprocessTimerElapsedMs()method computes(System.nanoTime() - processTimerNanos) / 1_000_000. Note thatnanoTime()can overflow after ~292 years of JVM uptime, and the subtraction handles this correctly due to two's complement arithmetic. No issue here, just noting for completeness. - [Info]
NettyDecoder.java— Clean removal of the GuavaStopwatchimport. Thelong timerNanos = System.nanoTime()is a drop-in replacement.
Suggestions
- Consider adding a brief comment on
HEADER_CTOR_CACHEexplaining why aConcurrentHashMapwas chosen overClassValue(which is specifically designed for per-class lazy computation).ClassValuewould avoid the explicit cache map and may have better GC characteristics. - The four changes are independent — if the maintainers prefer smaller PRs, these could be split. However, they are all in the same hot path and the PR description clearly separates them, so keeping them together is also reasonable.
Verdict
LGTM. Well-justified micro-optimizations with clear JFR evidence.
Automated review by github-manager-bot
0d43e12 to
03ee743
Compare
RockteMQ-AI
left a comment
There was a problem hiding this comment.
Review by github-manager-bot (re-review)
Summary
Four independent per-RPC micro-optimizations in the remoting framework:
- Guava
Stopwatch→System.nanoTime()(eliminates per-RPC object allocation) - Constructor cache for
Class.getDeclaredConstructor()(eliminates ~237 alloc events/60s in JFR) - Netty writability log downgrade INFO/WARN → DEBUG (eliminates ~900 lines/sec)
TopicQueueMappingContext.EMPTYsingleton (avoids per-message allocation for >99% of messages)
Findings
- [Positive]
RemotingCommand.java— ReplacingStopwatchwith a rawlong processTimerNanos+processTimerElapsedMs()is a clean elimination of per-RPC allocation. The accessor method encapsulates the conversion well. - [Positive]
RemotingCommand.java—HEADER_CTOR_CACHEwithConcurrentHashMap.computeIfAbsent()correctly avoids the per-callConstructorcopy. ThenewHeaderInstance()helper centralizes the reflective construction with proper error handling. - [Positive]
NettyRemotingServer.java— DowngradingchannelWritabilityChangedto DEBUG withisDebugEnabled()guard is the right call. ~900 lines/sec at INFO was clearly excessive. - [Info]
TopicQueueMappingContext.java— TheEMPTYsingleton is a simple and effective optimization. Ensure callers don't mutate it — consider making fields immutable or adding a comment documenting the shared-instance contract. - [Info] Cross-repo:
RemotingCommandchanges (especiallysetProcessTimerNanos/processTimerElapsedMs) touch the core remoting API. Ifapache/rocketmq-clientsJava module depends onRemotingCommand, verify no breakage there.
Verdict
Solid set of micro-optimizations with clear JFR evidence. No correctness concerns.
Automated review by github-manager-bot
03ee743 to
cb97b22
Compare
RockteMQ-AI
left a comment
There was a problem hiding this comment.
Review by github-manager-bot (re-review after new commit)
Summary
Same four per-RPC micro-optimizations as previously reviewed. The new commit squashes the changes into a single commit without altering the diff content.
Verdict
No changes since the previous review. All findings remain valid. Approving.
LGTM ✅
Automated review by github-manager-bot
…(Guava Stopwatch, Constructor copy) and downgrade Netty writability log - Replace Guava Stopwatch with System.nanoTime() to avoid allocation - Cache Constructor objects in ConcurrentHashMap to avoid reflection overhead - Downgrade Netty writability log from INFO to DEBUG - Add TopicQueueMappingContext.EMPTY singleton - Update broker processors to use processTimerElapsedMs() instead of getProcessTimer().elapsed() - Remove unused TimeUnit imports from processor files
cb97b22 to
3a84cc7
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #10514 +/- ##
=============================================
- Coverage 48.17% 48.15% -0.02%
+ Complexity 13394 13392 -2
=============================================
Files 1377 1377
Lines 100730 100755 +25
Branches 13012 13017 +5
=============================================
- Hits 48530 48522 -8
- Misses 46264 46283 +19
- Partials 5936 5950 +14 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Summary
Four independent per-RPC micro-allocations eliminated in the remoting framework:
1. Guava Stopwatch → System.nanoTime()
RemotingCommandusedStopwatch.createStarted()which allocates a new object per RPC. Replaced withlong processTimerNanos+processTimerElapsedMs().2. Constructor cache
Class.getDeclaredConstructor()copies theConstructorobject on every call (~237 allocation events per 60s JFR). AddedConcurrentHashMap<Class<?>, Constructor<?>> HEADER_CTOR_CACHEwithcomputeIfAbsentto pay the reflective lookup once per class.3. Netty writability log downgrade
NettyRemotingServer.channelWritabilityChangedlogged at INFO/WARN on every writability change (~81,434 lines in 90s = ~900 lines/sec). Downgraded to DEBUG withisDebugEnabled()guard.4. TopicQueueMappingContext.EMPTY singleton
Non-static-topic messages (>99%) created empty
TopicQueueMappingContextobjects. Addedpublic static final EMPTYsingleton.Files Changed
remoting/.../protocol/RemotingCommand.javanewHeaderInstance()+setProcessTimerNanos()/processTimerElapsedMs()remoting/.../netty/NettyDecoder.javaStopwatch→System.nanoTime()+setProcessTimerNanos()remoting/.../netty/NettyRemotingServer.javachannelWritabilityChangedINFO/WARN → DEBUG withisDebugEnabled()guardremoting/.../protocol/statictopic/TopicQueueMappingContext.javaEMPTYstatic singleton