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]

Reply via email to