Skip to content

[ISSUE #S2] Guard against a missing ext unit in ConsumeQueue.estimateMessageCount - #11100

Open
zjncs wants to merge 1 commit into
apache:developfrom
zjncs:fix/consume-queue-estimate-null-ext
Open

[ISSUE #S2] Guard against a missing ext unit in ConsumeQueue.estimateMessageCount#11100
zjncs wants to merge 1 commit into
apache:developfrom
zjncs:fix/consume-queue-estimate-null-ext

Conversation

@zjncs

@zjncs zjncs commented Sep 9, 2026

Copy link
Copy Markdown

Motivation

ConsumeQueue.estimateMessageCount dereferences the ext unit unconditionally:

ConsumeQueueExt.CqExtUnit ext = null;
if (isExtWriteEnable()) {
    ext = consumeQueueExt.get(tagCode);
    tagCode = ext.getTagsCode();   // NPE when get() returns null
}

The stored tagsCode can legitimately be a raw (positive) value rather than an ext address:

  • entries written while the ext was disabled and the config was enabled later, or
  • entries persisted by the save-tagsCode-only fallback in putMessagePositionInfoWrapper when consumeQueueExt.put fails (logged as "Save consume queue extend fail, So just save tagsCode").

For a raw code ConsumeQueueExt.get returns null (it rejects non-ext addresses), so estimateMessageCount throws an NPE. This path is live in production: DefaultMessageStore.estimateMessageCountConsumerLagCalculator (broker consumer-lag metrics with a filter). MessageFilter.isMatchedByConsumeQueue already accepts a null ext unit ("message is before consumer"), so only this caller assumes non-null.

Modifications

  • Null-guard the ext unit: keep the raw tagCode when get returns null and pass the null ext to the filter, matching how the rest of the code treats a missing ext.

Verification

Fail-before (new test on unpatched code):

ConsumeQueueTest.testEstimateMessageCountWhenExtUnitMissing:500 » NullPointer
  Cannot invoke "ConsumeQueueExt.getTagsCode()" because "ext" is null

The test writes one entry with the ext disabled (raw tagsCode persisted), re-enables the ext, then estimates the count — exactly the mixed-content state described above.

Pass-after:

mvn -pl store test -Dtest='ConsumeQueueTest#testEstimateMessageCountWhenExtUnitMissing+testCorrectMinOffset'
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

…MessageCount

estimateMessageCount unconditionally dereferences the ext unit when the
consume queue ext is enabled. The stored tagsCode can legitimately be a
raw value instead of an ext address (entries written while the ext was
disabled, or persisted by the save-tagsCode-only fallback when the ext
put fails), and ConsumeQueueExt.get returns null for such codes, so the
consumer-lag estimation blows up with an NPE. Keep the raw tagsCode and
let the filter handle the null ext unit, like the pull path already does.

Signed-off-by: zjncs <18910855655@163.com>
Copilot AI lite review requested due to automatic review settings September 9, 2026 10:31

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@RockteMQ-AI RockteMQ-AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Correct NPE fix for a real production path — estimateMessageCount dereferences the ext unit unconditionally, but ConsumeQueueExt.get() returns null for raw tagsCodes in mixed-content states. The null-guard preserves the raw tagCode and passes null ext to the filter, matching how the rest of the codebase handles missing ext units. Test reproduces the exact mixed-content scenario.


Automated review by github-manager

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants