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

   ## Problem
   
   `rmq.group.detail` grades a consumer group's health from three inputs — 
consume-stat availability, connection availability and the total lag. The 
ladder enumerates the first two "signal unavailable" cases but then compares 
the lag directly:
   
   ```java
   } else if (group.getOnlineInstances() == 0) {
       status = "WARNING";
       reasons.add("The group has no online consumer.");
   } else if (group.getTotalLag() > 0) {
       status = "WARNING";
       reasons.add("The group has accumulated messages.");
   } else {
       status = "HEALTHY";
   }
   ```
   
   `-1` is the lag-unknown sentinel (`ConsumerLagResolver.UNKNOWN`), so it is a 
*successful* comparison against `> 0` and the group is graded **HEALTHY** — a 
definite "nothing has accumulated" verdict derived from data the server marks 
undeterminable.
   
   ## Evidence
   
   The state is reachable with `consumeStatsAvailable == true`: 
`RocketMQMetadataProvider` sets it as soon as `examineConsumeStats` returns 
(`:347`) and then writes the sentinel when any queue diff is the broker's own 
`-1` for 5.0 gRPC consumers (`:368`, `lagUnknown ? ConsumerLagResolver.UNKNOWN 
: totalLag`). The same vocabulary is already used on the read side: 
`GroupDetailOutput.Progress.from` collapses a queue lag of `-1` into `totalLag 
= -1`, and the contract documents `-1` for `onlineInstances` as "the connection 
inventory is unavailable".
   
   Reproduction (added test, mock only): 
`ConsumerGroupVO{consumeStatsAvailable=true, onlineInstances=1, totalLag=-1}` → 
before the fix `health = {"status":"HEALTHY","reasons":[]}`; expected `UNKNOWN` 
with a reason.
   
   Existing coverage only pins the two other unavailable signals: 
`detailMarksHealthUnknownWhenConsumerConnectionsAreUnavailableTest` 
(`onlineInstances=-1`) and 
`detailWithoutOnlineConsumersKeepsProgressAndClientsEmpty` 
(`consumeStatsAvailable=false`). There was no `totalLag == -1` case.
   
   ## Root cause
   
   The verdict ladder was written when the total lag was always a measurement. 
The later "preserve unknown lag" work introduced the `-1` sentinel across the 
provider, the aggregate VO and the tool's own progress block, but the health 
ladder was not updated, so the sentinel falls through to the healthy branch.
   
   ## Fix
   
   Test the sentinel before the lag-derived branches and answer `UNKNOWN` with 
`"Consumer lag information is unavailable."`, mirroring the two existing 
unavailable-signal branches. The branch is placed after the connection-derived 
ones on purpose: `onlineInstances == 0` is a definite fact independent of the 
lag, so that group keeps its `WARNING` rather than degrading to `UNKNOWN`. Both 
boundaries are pinned by tests.
   
   ## Priority
   
   **60** = impact 18 (a wrong positive verdict is exactly what an operator or 
an agent acts on during triage; no data corruption) + blast radius 10 (one tool 
and its frozen MCP/JSON contract, used by the playground, the hosted agent and 
external MCP clients) + reproducibility 18 (deterministic, mock-only, no broker 
needed) + maintenance 14 (the ladder has to track the sentinel vocabulary the 
rest of the contract already uses).
   
   `FIX_CONFIDENCE` **88** — three lines plus tests; the only behaviour change 
is the group that was mis-graded.
   
   ## Tests
   
   Two new tests: `detailMarksHealthUnknownWhenTheConsumerLagIsUnavailableTest` 
**fails before the fix** (`expected: "UNKNOWN"`, actual `HEALTHY`), and 
`detailKeepsTheConnectionWarningWhenTheConsumerLagIsUnavailableTest` pins the 
boundary that stays `WARNING`.
   
   ```
   $ mvn -o test -Dtest=ConsumerGroupReadToolHandlersTest
   Tests run: 6, Failures: 0, Errors: 0, Skipped: 0
   BUILD SUCCESS
   
   $ mvn -o test -Dtest='org.apache.rocketmq.studio.ops.ai.tool.**'
   Tests run: 166, Failures: 0, Errors: 0, Skipped: 0
   BUILD SUCCESS
   (checkstyle:validate ran as part of both builds)
   ```
   
   ## Risk
   
   Low. The contract's `health.status` enum already includes `UNKNOWN`, so no 
schema change is needed; only a group whose lag is genuinely unresolved can 
change status, and it moves from an unjustified `HEALTHY` to an explicit 
`UNKNOWN` with a reason.


-- 
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