[ISSUE #10549] Fix lite topic reset offset: memory leak, FIFO block bypass, and offset-0 reset failure#10550
[ISSUE #10549] Fix lite topic reset offset: memory leak, FIFO block bypass, and offset-0 reset failure#10550f1amingo wants to merge 1 commit into
Conversation
…lock bypass, and offset-0 reset failure - Fix memory leak in removeResetOffset: clean up empty inner map entries from resetOffsetTable - Add eraseResetOffset for precise cleanup on lite topic removal - Skip FIFO block check in isFifoBlocked when server-side reset offset is pending - Fix ResetOffsetByTimeCommand: change resetOffset > 0 to >= 0 to allow resetting to offset 0 - Add unit tests for eraseResetOffset and isFifoBlocked reset bypass
RockteMQ-AI
left a comment
There was a problem hiding this comment.
Review by github-manager-bot
Summary
This PR fixes three related bugs in lite topic offset reset: memory leak in resetOffsetTable (empty inner maps not cleaned up), FIFO block bypass when a server-side reset offset is pending, and offset-0 being silently skipped during reset. All three fixes are well-scoped and include appropriate unit tests.
Findings
-
[Info]
ConsumerOffsetManager.java:466-480— ThecomputeIfPresentlambda in bothqueryThenEraseResetOffsetanderaseResetOffsetperforms a side-effect (map.remove(queueId)) on the innerConcurrentHashMapwithin the outer map's atomic computation. This is a well-known pattern and works correctly because the inner map is also concurrent, but consider adding a brief comment explaining the intentional side-effect for future maintainers. -
[Info]
ConsumerOffsetManager.java:477-481— The neweraseResetOffset(topic, group, queueId)method is clean and consistent with the updatedqueryThenEraseResetOffset. The return-null-from-lambda pattern to remove the outer key when the inner map becomes empty is idiomaticConcurrentHashMapusage. -
[Info]
PopLiteMessageProcessor.java:314-317— TheisFifoBlockedbypass correctly checkshasOffsetReset(lmqName, group, 0)with hardcoded queueId=0, which is appropriate for lite topics (single queue). The early returnfalseallows consumption to proceed from the reset position instead of being blocked by FIFO ordering — this is the correct behavior when a server-side reset is pending. -
[Info]
ResetOffsetByTimeCommand.java:143— ChangingresetOffset > 0toresetOffset >= 0is correct.searchOffsetcan legitimately return 0 (e.g., when the timestamp maps to the very first message), and silently skipping it was a bug. -
[Info]
AbstractLiteLifecycleManager.java:208— TheeraseResetOffset(lmqName, group, 0)call is correctly placed beforeremoveConsumerOffsetindeleteLmq, ensuring orphaned reset offset entries are cleaned up when a lite topic is removed.
Suggestions
- Consider adding a brief inline comment in
queryThenEraseResetOffsetexplaining why the lambda has a side-effect on the inner map, to prevent future refactoring from inadvertently breaking the cleanup logic. - The
eraseResetOffsetmethod could benefit from a null/empty check ontopic/groupparameters (similar toassignResetOffset's validation) for defensive programming, though this is optional since the caller already provides valid values.
Verdict
The fixes are correct, well-tested, and minimal. LGTM.
Automated review by github-manager-bot
RockteMQ-AI
left a comment
There was a problem hiding this comment.
Review by github-manager-bot
Summary
Fixes three related bugs in lite topic offset reset:
- Memory leak in
resetOffsetTable— empty inner maps not cleaned up after removing entries - FIFO block bypass — when
useServerSideResetOffsetis enabled and a reset offset is pending, consumption should proceed immediately - Reset to offset 0 silently skipped — boundary condition
> 0changed to>= 0
Findings
Bug 1: Memory leak fix
- [Info]
ConsumerOffsetManager.java:469-477—queryThenEraseResetOffsetnow cleans up empty inner maps viacomputeIfPresent. This prevents stale key accumulation inresetOffsetTable. - [Info]
ConsumerOffsetManager.java:480-486— NeweraseResetOffset(topic, group, queueId)method provides a clean API for removing reset offset entries without querying. - [Info]
AbstractLiteLifecycleManager.java:208— Called duringdeleteLmqto clean up orphaned reset offset entries when a lite topic is removed. Good integration.
Bug 2: FIFO block bypass
- [Info]
PopLiteMessageProcessor.java:314-316— WhenuseServerSideResetOffsetis enabled and a pending reset exists for queue 0,isFifoBlockedreturnsfalseimmediately, bypassing theconsumerOrderInfoManager.checkBlockcall. This allows consumption to proceed from the reset position.
Bug 3: Offset 0 boundary
- [Info]
ResetOffsetByTimeCommand.java:143— Simple> 0→>= 0fix. Offset 0 is a valid reset target (beginning of topic).
Suggestions
- [Warning]
ConsumerOffsetManager.java:471-473— ThecomputeIfPresentlambda returnsnullto remove the entry when the inner map is empty. This is correct perConcurrentMapsemantics (returningnullfromcomputeIfPresentremoves the mapping), but worth noting that this relies on the inner map being aConcurrentMapthat supports this pattern. The testtestEraseResetOffsetverifies this correctly. - [Info]
PopLiteMessageProcessor.java:314— The FIFO bypass is hardcoded to queue0. This matches the lite topic model where each lite topic uses a single queue, but if the model ever changes to multi-queue, this would need updating. Consider adding a brief comment explaining why queue 0 is assumed. - [Info] Tests are well-structured.
testEraseResetOffsetverifies both individual removal and empty-map cleanup.testIsFifoBlocked_hasResetOffsetcorrectly verifies thatcheckBlockis never called when a reset is pending.
Verdict
Well-structured multi-bug fix with focused changes and proper test coverage. Each fix is independent and addresses a real issue. LGTM.
Automated review by github-manager-bot
Which Issue(s) This PR Fixes
Brief Description
This PR fixes three related bugs in lite topic offset reset:
Memory leak in
resetOffsetTable:removeResetOffset()now cleans up empty inner map entries after removing a queueId, preventing stale key accumulation.FIFO block bypass on reset: When
useServerSideResetOffsetis enabled and a reset offset is pending,isFifoBlocked()returnsfalseimmediately, allowing consumption to proceed from the reset position.Reset to offset 0 silently skipped: Changed
resetOffset > 0toresetOffset >= 0inResetOffsetByTimeCommandso that offset 0 is a valid reset target.Additionally, a new
eraseResetOffset(topic, group, queueId)method is introduced and called during lite topic removal inAbstractLiteLifecycleManagerto clean up orphaned reset offset entries.How Did You Test This Change?
testEraseResetOffsetinConsumerOffsetManagerTestverifying precise removal and empty-map cleanup.testIsFifoBlocked_hasResetOffsetinPopLiteMessageProcessorTestverifying FIFO bypass when reset is pending.