[Bug] Consumers registered via heartbeat-v2 withoutSub are never removed by fast channel-close events
Before Creating the Bug Report
- I found a bug, not just asking a question, which should be created in GitHub Discussions.
- I have searched the GitHub Issues and GitHub Discussions of this repository and believe that this is not a duplicate.
- I have confirmed that this bug belongs to the current repository, not other repositories of RocketMQ.
Runtime platform environment
Linux, JDK 21, develop (ff8f6f74c)
RocketMQ version
5.x develop
Describe the Bug
With enableFastChannelEventProcess=true, channel-close cleanup is driven by the CHANNEL_CONSUMER_GROUP channel attribute that ConsumerManager#registerConsumer writes via ClientChannelAttributeHelper.addConsumerGroup(...) (ConsumerManager.java, the r1 branch).
ConsumerManager#registerConsumerWithoutSub — the path taken by heartbeat-v2 when the client's subscription fingerprint is unchanged (withoutSub=true) — also registers the channel in consumerTable via updateChannel, but never calls addConsumerGroup. Two consequences:
- A consumer whose registration on a channel arrives only via a withoutSub heartbeat (the normal case for a reconnecting client whose subscriptions did not change — e.g. broker restart or idle connection drop, MQClientInstance sends withoutSub=true on the new channel once the fingerprint matches) has no channel attribute.
doChannelCloseEvent's fast path iteratesClientChannelAttributeHelper.getConsumerGroups(channel), gets an empty list, and returns without removing anything. - A later full heartbeat on the same channel cannot repair this:
updateChannelreturns false for an already-registered channel, soregisterConsumer'sr1-guardedaddConsumerGroupis never reached.
The dead consumer entry then stays in consumerTable for up to channelExpiredTimeout (120s by default) until scanNotActiveChannel expires it, delaying UNREGISTER notifications (ConsumerFilterManager cleanup) and leaving stale entries in getConsumerConnectionList / consumer connection queries.
Steps to Reproduce
- Enable
enableFastChannelEventProcess. - Register a consumer group on a channel via
registerConsumerWithoutSub(heartbeat-v2 withoutSub). - Close the channel and fire
doChannelCloseEvent. - The group is still present in
consumerTable(returns false instead of true).
Expected Behavior
The channel-close fast path must remove consumer registrations whose registration came through registerConsumerWithoutSub, i.e. that method must maintain the same channel attribute as registerConsumer does.
Corresponding PR
- Fix PR: #11040 (linked with
Closes #11039in the PR description; contains the regression test that fails before the fix and passes after it).
Source: apache/rocketmq