unbridled-41 opened a new pull request, #5090:
URL: https://github.com/apache/rocketmq-dashboard/pull/5090

   ## Problem
   
   `TencentInstanceProvider.getTopicConsumers` pages `DescribeTopic` with 
`CONSUMER_PAGE_SIZE` (100) and ends the scan as soon as a page comes back short:
   
   ```java
   Long subscriptionCount = response.getSubscriptionCount();
   if (data.length < CONSUMER_PAGE_SIZE
           || subscriptionCount != null && fetchedSubscriptions >= 
subscriptionCount) {
       break;
   }
   ```
   
   The short-page test runs first, so a page that returns fewer rows than 
requested ends the scan **even when the response's own `SubscriptionCount` says 
more subscriptions exist**. The rows are dropped silently: the method returns 
what it fetched, and `TopicConsumerPageVO.total` is just `consumers.size()`, so 
the caller cannot tell the list is incomplete.
   
   ## Evidence
   
   Call path: `TopicController.getTopicConsumers` / 
`MetadataService.getTopicConsumers` → `InstanceProviderRegistry` → 
`TencentInstanceProvider.getTopicConsumers` 
(`TencentInstanceProvider.java:396-431`); also 
`ops/ai/tool/handler/topic/TopicDetailToolHandler`.
   
   Reproduction (stubbed client, in the added test): page 0 returns 40 
`SubscriptionData` while `SubscriptionCount = 250`. Observed: the call returns 
40 consumers and no error. Expected: HTTP 502 `"Tencent Cloud returned an 
incomplete topic consumer page"`, which is what the three sibling scans in this 
class already do.
   
   The siblings call `requireCompletePage(...)` and end their loops on 
`hasFetchedAll(...) || isUnknownTotalCount(...) && returned < PAGE_SIZE` — 
`listTopics` (`:222-238`), `listConsumerGroups` (`:434-467`), 
`getGroupSubscriptions` (`:1004-1027`). `34b03320` ("reject incomplete resource 
pages", #4531) added that guard to those three loops and missed this fourth 
one, which has the same shape (offset/limit paging plus a server-reported 
total). Each sibling has an incomplete-page regression test (`:217`, `:578`, 
`:776`); `getTopicConsumers` had none.
   
   ## Root cause
   
   The loop terminates on the page-length heuristic before consulting the total 
it already reads, so a short page is accepted as the end of the list instead of 
being validated against `SubscriptionCount`.
   
   ## Fix
   
   Mirror the sibling loops: compute the page offset, call 
`requireCompletePage("topic consumer", offset, data.length, subscriptionCount)` 
before continuing, and break only when the reported total is reached 
(`hasFetchedAll`) or when the total is unknown and the page is short 
(`isUnknownTotalCount`). The now-redundant `fetchedSubscriptions` accumulator 
is removed since the completion test is the same `offset + returned` expression 
the siblings use.
   
   ## Priority
   
   **54** = impact 15 (silent data loss in an inventory read: a subscribed 
group is reported as "not subscribed", which is what an operator or the AI tool 
acts on) + blast radius 10 (Tencent instances only; topic detail page and 
`rmq.topic.detail`) + reproducibility 16 (deterministic with a stub; production 
needs a genuinely short page) + maintenance 13 (closes the residual of an 
incomplete fix whose guard and tests already exist as a pattern).
   
   `FIX_CONFIDENCE` **92**.
   
   ## Tests
   
   New 
`getTopicConsumersShouldRejectIncompletePageWhenTotalCountRequiresMoreTest` 
**fails before the fix** (`Expecting code to raise a throwable`) and passes 
after.
   
   ```
   $ mvn -o test -Dtest='org.apache.rocketmq.studio.provider.tencent.*Test'
   Tests run: 85, Failures: 0, Errors: 0, Skipped: 0
   BUILD SUCCESS
   ```
   
   The three pre-existing `getTopicConsumers` tests (map, read every page, stop 
on a full page whose count is reached) still pass, which is what pins the 
unchanged behaviour for complete pages.
   
   ## Risk
   
   Low. A 502 now surfaces where a truncated list was returned before; that is 
the same contract the topic-list, consumer-group-list and subscription scans 
already enforce for the same API family, and the error is a `BusinessException` 
the shared handler already renders. A stub that returns a short page without a 
`SubscriptionCount` (or with an unknown total) keeps the previous behaviour.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to