[ISSUE #10700] Make consumer offset commits concurrency-safe - #10701
[ISSUE #10700] Make consumer offset commits concurrency-safe#10701ai-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 real concurrency race in both ConsumerOffsetManager and RocksDBConsumerOffsetManager where concurrent first-offset commits for the same topic@group could overwrite each other's inner maps, losing queue offsets. The fix uses putIfAbsent for atomic map initialization and synchronized(map) for incremental WAL persistence ordering.
Findings
-
[Positive]
RocksDBConsumerOffsetManager.java:195-201— TheputIfAbsent+ winner-takes-all pattern correctly eliminates the check-then-act race. The originalget()→put()sequence could indeed lose queues under concurrent first commits. -
[Positive]
RocksDBConsumerOffsetManager.java:207-216— Thesynchronized(map)block correctly serializes incremental persistence for the same key, preventing an older WAL batch from being written after a newer one. This is the right granularity — per-key, not global. -
[Positive] Non-incremental path remains lock-free across different
topic@groupkeys, preserving throughput for the common case. -
[Info]
RocksDBConsumerOffsetManager.java:203-206— In the non-incremental path,updateOffset()is called without synchronization. Two concurrent commits for the same key could both callmap.put()and both evaluate the "less than store" warning. SinceConcurrentHashMap.put()is atomic, this is functionally safe, but the warning log might be slightly misleading under contention. This matches the original behavior and is not a regression. -
[Info]
ConsumerOffsetManager.java— The classic manager gets the sameputIfAbsenttreatment. Good that both paths are fixed consistently. -
[Positive] Test coverage is thorough — deterministic red tests that demonstrate the race on the old code, plus full broker suite validation (755 tests). The reference to historical unmerged PR #1427 shows good due diligence.
Suggestions
-
Consider adding a brief comment on the
synchronized(map)block explaining why we synchronize on the inner map (WAL ordering for same RocksDB key), so future maintainers don't accidentally remove it. -
The
updateDataVersionIfNeeded()helper is a nice extraction. One minor note: in the non-incremental path it's called outside any synchronization, while in the incremental path it's insidesynchronized(map). This is correct for the current logic but worth a one-line comment explaining the difference.
Verdict
Solid concurrency fix with good test coverage. The approach is sound and the scope is well-contained.
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
Signed-off-by: Rui <[email protected]>
c55c6d6 to
03695c8
Compare
|
Refreshed this PR onto the current
The production change is +37/-20 lines; +261/-6 lines are deterministic concurrency/persistence tests. Full CI has been retriggered. @RongtongJin @lizhimins, could you please take a human review when convenient, especially of same-key WAL ordering and the classic/RocksDB v1 initialization paths? |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #10701 +/- ##
=============================================
- Coverage 48.58% 48.47% -0.11%
+ Complexity 13678 13647 -31
=============================================
Files 1381 1381
Lines 101475 101486 +11
Branches 13190 13192 +2
=============================================
- Hits 49304 49198 -106
- Misses 46170 46266 +96
- Partials 6001 6022 +21 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
RockteMQ-AI
left a comment
There was a problem hiding this comment.
Summary
This PR modifies 4 files (442 lines).
Key changes reviewed. Please see inline comments for specific suggestions.
Automated review by "github-manager-bot"
Which Issue(s) This PR Fixes
Brief Description
Make first offset-map initialization atomic in the classic and RocksDB v1 consumer offset managers, and serialize incremental whole-map persistence for the same
topic@group.putIfAbsent, then update the selected winner map.topic@groupkeys to proceed independently.Root Cause
Classic and RocksDB v1 used check-then-act initialization followed by unconditional outer
put, so two first commits could publish different inner maps and overwrite one queue. RocksDB v1 incremental mode additionally allowed an older serialized whole-map batch to be written after a newer batch for the same RocksDB key.Historical unmerged PR #1427 identified the classic initialization race. This PR preserves that analysis while extending coverage to RocksDB v1, incremental WAL ordering, and deterministic persistence/reload tests. Open PRs #10625, #9602, and #9877 touch related files but do not change these commit paths.
Impact
Concurrent first commits retain every queue in memory and after persistence. Incremental restart recovery cannot regress to an older same-key snapshot. Existing rollback warnings, version semantics, LMQ allocation, and v2 behavior are preserved.
How Did You Test This Change?
ConsumerOffsetManagerTest+RocksDBConsumerOffsetManagerTest: 21 tests passed.brokertest suite: 755 tests passed, 0 failures, 0 errors, 4 skipped.git diff --check.