Skip to content

fix(ai): populate subscriptionMode and consumeType for cloud consumer groups - #4134

Closed
zjncs wants to merge 1 commit into
apache:rocketmq-studiofrom
zjncs:fix/cloud-group-subscription-mode
Closed

fix(ai): populate subscriptionMode and consumeType for cloud consumer groups#4134
zjncs wants to merge 1 commit into
apache:rocketmq-studiofrom
zjncs:fix/cloud-group-subscription-mode

Conversation

@zjncs

@zjncs zjncs commented Sep 8, 2026

Copy link
Copy Markdown

Motivation

The rmq.group.list AI tool is deterministically broken for Aliyun and Tencent instances:

  • ConsumerGroupListToolHandler.safeProjection requires both subscriptionMode and consumeType (requiredEnumName throws IllegalStateException on the first null).
  • AliyunConverters.toConsumerGroupVO never sets subscriptionMode and its toConsumeType returns null when the OpenAPI response omits messageModel.
  • TencentInstanceProvider.toConsumerGroup never sets subscriptionMode either (its consume type is always CLUSTERING).

So the first group of any cloud instance trips IllegalStateException: Consumer group subscriptionMode is unavailable: <name> and the whole tool call fails.

This contradicts the invariant the Apache provider already established in RocketMQMetadataProvider.toConsumerGroupVO:

// messageModel stores the subscription mode ("Push"/"Pop"); surface it so read paths
// (web detail, AI rmq.group.list) never see a null subscriptionMode.
vo.setSubscriptionMode(parseSubscriptionMode(entity.getMessageModel()));

The invariant was fixed for Apache only; both cloud converters were missed. Existing tests never catch it because they always pre-set the enum (e.g. ToolGatewayServiceTest builds groups with SubscriptionMode.Push).

Modification

  • Both cloud converters emit SubscriptionMode.Push: cloud TCP consumer groups are push consumers (Aliyun's messageModel carries the consume model, not the subscription mode; Tencent has no POP groups).
  • AliyunConverters.toConsumeType falls back to CLUSTERING for a missing/unrecognized messageModel, mirroring RocketMQMetadataProvider.parseConsumeType ("falling back to CLUSTERING").

Verification

mvn -f server/pom.xml test -Dtest='AliyunInstanceProviderTest,TencentInstanceProviderTest'

fail-before (fix reverted, tests kept):

AliyunInstanceProviderTest.listConsumerGroupsShouldMapGroupIdTest         <<< FAILURE! (subscriptionMode was null)
AliyunInstanceProviderTest.listConsumerGroupsShouldFallBackWhenMessageModelMissingTest <<< FAILURE!
TencentInstanceProviderTest.listConsumerGroupsShouldMapAndFilterTest      <<< FAILURE! (subscriptionMode was null)

pass-after:

TencentInstanceProviderTest: Tests run: 43, Failures: 0
AliyunInstanceProviderTest: 27 tests, only getGroupProgressShouldMapLagRowsTest failing —
    verified pre-existing on a clean rocketmq-studio checkout (unrelated to this change)

Follow-up to the Apache-side invariant; no associated issue — found by code inspection.

… groups

The Aliyun and Tencent converters never set subscriptionMode on
ConsumerGroupVO, and AliyunConverters.toConsumeType returns null when the
API omits messageModel. ConsumerGroupListToolHandler (the rmq.group.list
AI tool) treats both fields as required and throws IllegalStateException
on the first null, so listing consumer groups is deterministically broken
for every cloud instance - the exact invariant the Apache provider
already documents ('surface it so read paths (web detail, AI
rmq.group.list) never see a null subscriptionMode').

Cloud TCP consumer groups are push consumers, so both converters now emit
SubscriptionMode.Push, and the Aliyun consume-type mapping falls back to
CLUSTERING for a missing/unrecognized messageModel, mirroring
RocketMQMetadataProvider.parseConsumeType.

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

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

Choose a reason for hiding this comment

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

LGTM. Trivial change, looks good.


Automated review by github-manager-bot

lizhimins pushed a commit that referenced this pull request Sep 9, 2026
…data (#4115)

The AI resource tool chain was broken end to end: every one of `rmq.topic.list` and `rmq.group.list` failed, and for cloud instances the failure happened even once routing was fixed. Four separate defects, each fixed here, plus one stale test that hid a contract drift.

**Routing.** Both handlers passed the tool's `cluster` input into the `instanceId` slot — `listTopicsPage(clusterId, null, ...)` — so the request never reached the `!hasText(instanceId) && hasText(clusterId)` cluster-scoped branch. Because `cluster` is `required` with `minLength: 1` in the catalog, the legacy branch was unreachable and both tools threw `404 Instance not found: DefaultCluster` on every call. The arguments are now `(null, clusterId, ...)`. Regression introduced by #3052.

**Output schema.** `rmq-tools.yaml` declared `cluster.proxies`, `stats.totalProxies`, `stats.totalNameServers` and `items[].totalLag` as `type: integer` while keeping them `required`, but the handlers deliberately emit `null` to mean "unknown" — `totalLag` does so for `ConsumerLagResolver.UNKNOWN` since #3988. `ToolGatewayService.validateOutput` preserves null keys through `valueToTree`, so listing any group with an unknown lag raised `IllegalStateException` and returned 500. Those four are now `type: [integer, 'null']`; they are the complete reachable set of integer output fields whose producers can emit null.

**Cloud topic type and perm.** The Aliyun converter never set `perm` at all, so every Aliyun topic carried a null perm and `TopicListToolHandler.safeProjection` threw on its first `requiredEnumName` call. Both cloud `toTopicType` implementations also returned null for a blank or unrecognised type. They now guarantee non-null: `TopicPerm.RW` for Aliyun (its ListTopics API returns no permission field, and console-created cloud topics are read-write, matching the Tencent provider) and `TopicType.NORMAL` as the fallback, mirroring the Apache provider's `parseTopicType`. The fields that no provider ever populates — `messageCount`, `tps`, `consumerGroupCount` — are deliberately untouched.

**Cloud consumer group subscription mode.** Neither cloud provider set `subscriptionMode`, and Aliyun's `toConsumeType` returned null unless `messageModel` was exactly "Clustering", so `requiredEnumName` threw for every cloud consumer group. Both now set `SubscriptionMode.Push` — the pinned `alibabacloud-rocketmq20220801` response body has no subscription-mode field at all, and cloud TCP groups are push consumers — and `toConsumeType` returns `CLUSTERING`.

**Test contract drift.** `AliyunInstanceProviderTest.getGroupProgressShouldMapLagRowsTest` still expected a per-topic row *and* a `broker="total"` aggregate row, but `AliyunConverters.toQueueProgressRows` has only emitted the aggregate as a fallback when there are no topic rows since #2907. The expectation was stale, not the production code: emitting both would double-count lag, because `CloudRocketMqBusinessMetricsCollector` sums `getDiffTotal()` across every row it is given. The test now asserts the fallback-only contract and a second case covers the no-topic-breakdown path. This was one of the three standing backend red lights.

Folded in from #4117, #4134, #4135 and #4137, all by the same author and all part of this one chain; those PRs are closed as superseded. Merging any single one of them would not have made the tools usable.
@lizhimins

Copy link
Copy Markdown
Member

Closed as folded into #4115, merged into rocketmq-studio as 206314b.

Your change shipped as-is: both cloud providers now set SubscriptionMode.Push, and Aliyun's toConsumeType returns CLUSTERING instead of null when messageModel is absent or not Broadcasting, so requiredEnumName no longer throws for every cloud consumer group. Your reasoning for hardcoding Push held up — the pinned alibabacloud-rocketmq20220801 response body carries no subscription-mode field at all, and it matches what the Apache provider already asserts for its own groups.

This one had to be integrated rather than merged in sequence: it and #4137 both add an import to AliyunConverters.java and both extend AliyunInstanceProviderTest / TencentInstanceProviderTest, so whichever landed second needed a rebase. Putting both on one branch resolved that once, and both sets of tests pass together (44 Tencent, 29 Aliyun).

Nothing further needed from you — thank you for the fix. For context on why these were grouped: the AI resource tool chain had four independent defects (argument-slot routing, output-schema nullability, cloud topic type/perm, cloud consumer-group subscription mode) and merging any single one of them would not have made rmq.topic.list or rmq.group.list usable, so they were integrated as one change on the target PR's branch, each kept as its own commit with you as the author.

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.

4 participants