Skip to content

[ISSUE #11039] Track consumer group channel attribute for heartbeat-v2 withoutSub registration - #11040

Open
unbridled-41 wants to merge 1 commit into
apache:developfrom
unbridled-41:fix/without-sub-channel-attr
Open

[ISSUE #11039] Track consumer group channel attribute for heartbeat-v2 withoutSub registration#11040
unbridled-41 wants to merge 1 commit into
apache:developfrom
unbridled-41:fix/without-sub-channel-attr

Conversation

@unbridled-41

@unbridled-41 unbridled-41 commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Problem / Evidence

With enableFastChannelEventProcess=true, the fast channel-close path in ConsumerManager#doChannelCloseEvent cleans up a consumer channel by iterating ClientChannelAttributeHelper.getConsumerGroups(channel). That attribute is only written in the full registerConsumer (r1 branch). registerConsumerWithoutSub — the path taken by heartbeat-v2 whenever the client's subscription fingerprint is unchanged (withoutSub=true, the normal reconnect case after broker restart / idle connection drop) — registers the channel in consumerTable but never writes the attribute.

Consequences (verified in code):

  • A consumer registered on a channel only via withoutSub heartbeats is never removed by the fast close path: getConsumerGroups returns empty, the cleanup loop is skipped, doChannelCloseEvent returns false.
  • A later full heartbeat on the same channel cannot repair it, because updateChannel returns false for an already-registered channel, so registerConsumer's r1-guarded addConsumerGroup is not reached.

The dead entry lingers in consumerTable until scanNotActiveChannel expires it (channelExpiredTimeout, default 120s), delaying UNREGISTER (ConsumerFilterManager cleanup) and leaving stale entries in consumer connection queries.

Regression test ConsumerManagerTest#testWithoutSubRegistrationRemovedOnFastChannelClose fails before the fix (removed == false, group still in consumerTable) and passes after.

Root cause / Fix

Mirror the registerConsumer attribute bookkeeping in registerConsumerWithoutSub: call ClientChannelAttributeHelper.addConsumerGroup(channel, group) when the channel was added/updated and fast channel event processing is enabled.

Priority

PRIORITY = 72:影响 26(broker 重启/连接重建场景下失效消费者在 consumerTable 残留至 120s,延迟 UNREGISTER 通知与过滤器清理,污染连接列表——心跳 v2 常规路径)+ 波及范围 12(ConsumerManager fast 路径,单方法)+ 可复现性 20(确定性单元测试,EmbeddedChannel)+ 维护价值 14(与 registerConsumer 既有模式对齐,修复极小)。FIX_CONFIDENCE = 85。

Tests

  • mvn -pl broker test -Dtest=ConsumerManagerTest#testWithoutSubRegistrationRemovedOnFastChannelClose
    • before fix (ff8f6f7 + test only): Tests run: 1, Failures: 1
    • after fix: Tests run: 1, Failures: 0
  • mvn -pl broker test -Dtest=ConsumerManagerTest: 14/14
  • mvn -pl broker test -Dtest=ConsumerManagerScannerTest,ClientManageProcessorTest: 9/9

Risk

Low. The added attribute write only occurs on the updateChannel == true branch (new channel), same condition as the existing registerConsumer bookkeeping; ClientChannelAttributeHelper.addConsumerGroup is a no-op for inactive channels. The attribute is consumed only by the fast close path, so behavior without enableFastChannelEventProcess is unchanged.

Closes #11039

…beat-v2 withoutSub registration

registerConsumerWithoutSub registers the channel in consumerTable but
never calls ClientChannelAttributeHelper.addConsumerGroup, so with
enableFastChannelEventProcess=true the fast channel-close path finds no
group for the channel and leaves the dead consumer registered for up to
channelExpiredTimeout (120s). A later full heartbeat cannot repair it
because updateChannel returns false for an existing channel. Mirror the
registerConsumer attribute bookkeeping.
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 50.00000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 48.78%. Comparing base (ff8f6f7) to head (755debe).

Files with missing lines Patch % Lines
...apache/rocketmq/broker/client/ConsumerManager.java 50.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##             develop   #11040      +/-   ##
=============================================
- Coverage      48.86%   48.78%   -0.09%     
+ Complexity     13796    13769      -27     
=============================================
  Files           1381     1381              
  Lines         101574   101576       +2     
  Branches       13213    13214       +1     
=============================================
- Hits           49636    49549      -87     
- Misses         45925    45981      +56     
- Partials        6013     6046      +33     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@unbridled-41
unbridled-41 marked this pull request as ready for review September 5, 2026 11:32
@unbridled-41

Copy link
Copy Markdown
Contributor Author

Evidence chain (audit 2026-09-05):

Before the fixff8f6f7 + regression test only (production change stashed):

mvn -pl broker test -Dtest=ConsumerManagerTest#testWithoutSubRegistrationRemovedOnFastChannelClose
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0

The test uses a real EmbeddedChannel; doChannelCloseEvent returns removed == false and the group stays in consumerTable, because ClientChannelAttributeHelper.getConsumerGroups(channel) is empty without the attribute.

After the fix — branch tip 755debe, re-measured 2026-09-05:

mvn -pl broker test -Dtest=ConsumerManagerTest,ConsumerManagerScannerTest,ClientManageProcessorTest
Tests run: 23, Failures: 0, Errors: 0, Skipped: 0

Claim verification for "a later full heartbeat on the same channel cannot repair it": ConsumerGroupInfo#updateChannel returns updated = true only when the channel is new (broker/src/main/java/org/apache/rocketmq/broker/client/ConsumerGroupInfo.java:142-149 — for an existing channel only the clientId-mismatch branch runs, still returning false), so a full registerConsumer on the already-registered channel takes the r1 = false branch and the r1-guarded addConsumerGroup (ConsumerManager.java) is never reached. Verified by reading the code.

@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

Tracks the consumer group channel attribute during heartbeat-v2 withoutSub registration, ensuring the channel is correctly associated even when no subscription update is needed. Small, focused fix with appropriate test.

LGTM.


Automated review by github-manager-bot

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.

[Bug] Consumers registered via heartbeat-v2 withoutSub are never removed by fast channel-close events

3 participants