123123213weqw opened a new pull request, #5758: URL: https://github.com/apache/rocketmq-dashboard/pull/5758
## What\n\n`AgentStreamSession.finishReplay` flipped the session to LIVE and copied the buffered frames out under `sendLock`, but then drained them (`pending.forEach(deliver)`) **outside** the lock, with each frame's `sendAgent` re-taking the lock individually.\n\n## Why it is a bug\n\nA publisher thread (`AgentRunRegistry.publish` fan-out) that hands a live frame to the same session while the drain sits between two buffered frames contends for the lock per-frame, and can win it: the later frame reaches the wire before an older buffered one. The client folds frames in arrival order, so the transcript renders the later block ahead of the older one — a `tool_done` before its `tool_start`, an answer before the reasoning that preceded it. This window opens exactly at the replay-to-live handover, i.e. on every reconnect with frames in flight.\n\n## The fix\n\nDrain while holding `sendLock`. The intrinsic lock is reentrant, so `deliver`'s own `synchronized` blocks nest; a publisher no w parks until the whole drain finished, and arrival order on the wire is the buffer order followed by the live frames.\n\n## Test\n\nRed-first, in the new `AgentStreamSessionTest`: two frames are buffered during REPLAYING, and while the first is being written a second thread delivers a later frame (parked on the lock first, so the two threads genuinely race for it, repeated 50 times). Asserts the wire order is `older, newer, published` on every attempt — fails on the previous code (the later frame overtakes), passes with the drain under the lock. `AiStreamControllerTest` stays green. -- 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]
