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

   ## Problem
   
   The dead-letter message list of a group reports a complete-looking list and 
a complete-looking total even when the scan behind it was cut short. A queue 
the broker refuses to answer disappears from the list with no trace, and a 
group with more than 5000 dead letters inside the queried window silently loses 
the rest behind an unreachable page range.
   
   The two export endpoints of the same provider already carry the flags that 
describe this; the list endpoint, which is the screen an operator actually 
recovers from, does not.
   
   ## Evidence
   
   Base commit `1ef5d860`, 
`server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQDLQProvider.java`.
   
   * `:349-356` (base) — `listMessages` slices its page out of a bounded scan 
and returns a plain page:
     ```java
     List<DLQMessageVO> all = collectDeadLetters(instanceId, dlqTopic, begin, 
end, RESEND_HARD_CAP)
             .messages().stream()
             .map(this::toExportVO)
             .toList();
     ...
     return PageResult.of(all.subList(from, to), all.size(), page, pageSize);
     ```
     The `DeadLetterScanResult` produced at `:349` (`record` at `:701-706`) 
carries `truncated()` and `failedQueueCount()`, and neither is read here.
   * `:83` (base) — `RESEND_HARD_CAP = 5000`; the scan sets `truncated = true` 
at `:470` and `:513` when it stops at the cap, and increments 
`failedQueueCount` at `:476` and `:520` when a queue returns no pull result or 
throws, then keeps collecting the rest (only an all-queues failure throws).
   * Contrast the export paths, which do surface them: `exportMessages` at 
`:396-408` and `exportExcel` at `:369-393` build 
`DLQExportResultVO`/`DLQExcelExportResultVO` with `truncated`, 
`failedQueueCount`, `limit`, and `DLQController` returns them as 
`X-DLQ-Export-Truncated` / `-FailedQueues` / `-Limit` response headers 
(`DlqExportHeaders`, exposed via `CorsConfig`).
   * The web client already renders that warning for exports: 
`web/src/pages/instance/dlq.tsx:318` and `:430` (base) show `导出可能不完整:… 
个队列无法扫描,导出上限 … 条`.
   
   Trigger: `GET 
/api/dlq/%DLQ%group-a/messages?instanceId=…&page=1&pageSize=20` against a group 
with more than 5000 dead letters in the window, or one whose queue read fails. 
Today the response is `{items, total: 5000, page, size}` and the drawer shows a 
full-looking list with `total` as the pager's maximum.
   
   ## Root cause
   
   The list path dropped the scan metadata its own scan produces and its 
sibling export paths deliberately expose. `total` was then a bounded snapshot 
size presented as the queue size, and a partial scan was indistinguishable from 
a complete one.
   
   ## Fix
   
   * New `DLQMessagePageVO` — `items`, `total`, `page`, `size` plus 
`truncated`, `failedQueueCount`, `limit` — mirroring the export VOs.
   * `DLQProvider.listMessages`, `DLQProviderStub.listMessages`, 
`DLQService.listMessages` and `DLQController.listDLQMessages` now carry it; 
`RocketMQDLQProvider.listMessages` fills the flags from the 
`DeadLetterScanResult` it already had.
   * `MessageQueryDlqToolHandler` reads `items`/`total` from the new VO. The AI 
tool's *output contract* is deliberately unchanged — adding a field to a 
validated tool schema is a separate contract decision.
   * The DLQ drawer shows the same warning its export path shows, when the scan 
was truncated or a queue could not be read.
   
   ## Scoring (AGENTS.md)
   
   * PRIORITY **64** = impact 22 (silent row loss plus a wrong total on an 
operational recovery screen; the operator may conclude a group is clean) + 
reach 16 (the DLQ message list endpoint and the AI `rmq.dlq.query` tool both 
read it) + reproducibility 16 (deterministic: a queue stubbed to fail 
reproduces it) + maintenance value 10.
   * FIX_CONFIDENCE **72** — the direction is clear and mirrors the export 
path, but the response shape is an API decision; a reviewer could prefer a 
nested page or new headers, which is why this is below 80.
   
   ## Tests
   
   * `RocketMQDLQProviderTest.listMessagesReportsTheQueuesItCouldNotReadTest` — 
two queues, one unreadable; asserts one message, `failedQueueCount = 1`, 
`truncated = false`, `limit = 5000`.
   * `DLQPage.test.tsx` › `warns that the dead-letter list is incomplete when 
the scan was not` — `truncated: true, failedQueueCount: 2`; asserts the drawer 
warning.
   
   Pre-fix evidence is a probe, because the fix changes a return type so the 
typed regression test cannot compile against the old code. The probe calls 
`provider.listMessages` with one of two queues failing and asserts the 
serialised payload has the flags:
   
   ```
   # with only the production files restored to 1ef5d860, probe kept
   DLQListShapeProbeTest.theListPayloadCarriesTheScanFailureCount -- <<< 
FAILURE!
   org.opentest4j.AssertionFailedError:
   Expecting value to be true but was false
           (assertThat(json.has("failedQueueCount")).isTrue())
   
   # same probe on the branch
   tests="1" errors="0" skipped="0" failures="0"
   ```
   
   Web, with only `dlq.tsx` restored to `1ef5d860` and the new test kept:
   
   ```
   FAIL src/pages/instance/__tests__/DLQPage.test.tsx > warns that the 
dead-letter list is incomplete when the scan was not
   TestingLibraryElementError: Unable to find an element with the text: 
死信消息可能不完整.
   ```
   
   Post-fix, from the branch:
   
   ```
   mvn -o test 
-Dtest='RocketMQDLQProviderTest,DLQControllerTest,DLQServiceTest,MessageQueryDlqToolHandlerTest,AliyunInstanceProviderTest'
   RocketMQDLQProviderTest 40/40, DLQControllerTest 24/24, DLQServiceTest 
19/19, MessageQueryDlqToolHandlerTest 2/2
   
   npx vitest run src/pages/instance/__tests__/DLQPage.test.tsx   → 22/22
   npx tsc --noEmit                                               → clean
   npx eslint <touched files>                                     → 0 errors (1 
pre-existing react-refresh warning at dlq.tsx:82)
   ```
   
   Full web suite on the branch: see the verification comment.
   
   ## Risk
   
   The list response gains three fields; `items`, `total`, `page` and `size` 
keep their names and meaning, so existing consumers only need the new type. The 
AI tool output is untouched. The list still scans at most 5000 messages — this 
change makes that bound visible instead of removing it.
   


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