unbridled-41 opened a new issue, #4963: URL: https://github.com/apache/rocketmq-dashboard/issues/4963
### Before Creating the Bug Report - [x] I have searched the [open issues](https://github.com/apache/rocketmq-dashboard/issues) of this repository and believe that this is not a duplicate. Searched for `stream reconnect`, `SSE replay`, `run stream replay missing`, `agent run reconnect`, `replay persisted events` and `重连 回放`. The nearest are #4614 (closed: stop after a reconnect, fixed by #4613), #4866 (the final timeline refresh failing), #4922 (the optimistic bubble) and #4697 (closed: attaching across conversations) — all different defects. - [x] This is a defect in RocketMQ Studio, not a usage question and not a defect in another Apache RocketMQ repository. - [x] I can reproduce this on the current `master` branch, or I have stated the exact version I am running below. ### Studio Version branch: `rocketmq-studio` git commit id: `1ef5d860` (the revision this was written against; the fix is in PR #4948, branched from that commit) deployed as: not required for the reproduction — see Runtime Environment. ### Runtime Environment Reproduced with the backend unit tests (`cd server && mvn -o test -Dtest=AiRunServiceTest`), which need no deployment, no MySQL and no cluster. The defect is in the reconnect path's own logic, so a JVM is the only requirement. ### Connected RocketMQ Cluster Not involved. The events are persisted in Studio's own `rmq_ai_event` table; no RocketMQ cluster is contacted on this path. ### Describe the Bug A client that reconnects to a running agent run can lose the middle of its transcript permanently. `AiRunService.attach` (the reconnect path, behind `GET /api/ai/runs/{runId}/stream?after=<cursor>`) replays the persisted events after the client's cursor, but it reads **one page** of them and then raises the observer's dedup watermark to the last row of that page. Rows beyond the first page are therefore neither replayed nor sent live: * they sit behind the client's cursor, so the tail will never send them; * they were published before this observer existed, and `AgentRunRegistry` keeps no backlog, so nothing buffered them; * and `AgentStreamSession.deliver` drops any live frame at or below the watermark anyway. The page size is `AiConversationService.DEFAULT_TIMELINE_LIMIT` = 200, which is the timeline *display* page size rather than a replay bound. A long tool-heavy run persists two rows per tool call plus one per coalesced text or thinking block (`AgentEventProjector.COALESCE_MAX_CHARS` = 2048), so a client that slept, lost its connection or reloaded for a few minutes comes back to more than one page. ### Steps to Reproduce 1. `cd server && mvn -o test -Dtest=AiRunServiceTest#attachShouldReplayABacklogLongerThanOneTimelinePageTest` on `1ef5d860`. 2. The test stubs the event repository to return a full 200-row first page and a 201st row for the next page, then calls `attach(runId, 0)` — i.e. a client whose cursor is at the start of a run with 201 persisted events — and asserts that the frames for all 201 rows reach the SSE emitter. 3. The run fails: the 201st row's frame is never emitted. The same shape is asserted in `attachShouldNotLoseAFramePublishedWhileTheReplayIsReadTest`, which publishes a live frame for the run *while* the replay read is in flight: the frame is lost for the same reason, because the observer is registered after the read instead of before it. That also contradicts the method's own javadoc ("The observer is registered before the replay is flushed … nothing is lost in the gap"). ### What Did You Expect to See? Reconnecting replays every persisted event after the client's cursor, so the transcript the client rebuilds is the one the database holds ("先回放 `seq > after` 的已落库事件", `docs/api-spec.md` §15.8). A frame published while the replay is being loaded must not fall between the replay and the tail. ### What Did You See Instead? Only the first 200 rows are replayed. The rest are silently absent from the live transcript until the run finishes and the client refetches the timeline, so during a long run the answer the user is watching has a hole in it. ### Additional Context Code: `AiRunService.java:263-264` (single read) and `:275` (registration after the read) at `1ef5d860`; `AiConversationService.java:105`; `MybatisPlusAiEventRepository.java:59-63`; `AgentStreamSession.java:152,208`; `AgentRunRegistry.java:121-134`. Client side that supplies the cursor: `web/src/pages/ai/hooks/useActiveRunAttach.ts:58` → `web/src/api/ai.ts:407` (`after=<lastSeq>`). Corresponding pull request: #4948 (`fix(ai): drain a reconnect backlog that is longer than one timeline page`), which registers the observer before the read, drains page by page until a short page proves the backlog is empty, and adds the two tests above. ### Are You Willing to Submit a Pull Request? - [x] Yes, I am willing to submit a pull request. -- 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]
