Repository navigation
fix: honor Last-Event-ID on job log stream reconnects - #377
Conversation
The /api/jobs/:id/stream SSE handler always started tailing the stdout file at byte 0 on a new connection. Browsers auto-reconnect a dropped EventSource and resend the last Last-Event-ID they saw, but since that header was ignored, any reconnect (idle proxy timeout, tab suspend, brief network blip) re-streamed the whole log from the start. The client appends rather than replaces, so this looked like the full log replaying in a loop. Now each event carries the cumulative byte offset as its id, and a reconnecting request's Last-Event-ID seeds the starting offset so it only receives bytes past what it already saw.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe job stdout SSE endpoint now supports reconnects with ChangesStdout SSE resumption
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/dashboard/server.rs`:
- Around line 990-997: The current assertions in the stream resume test are too
loose and allow incorrect behavior to pass. Replace the existing assertions that
check for contains and does-not-contain with stricter validations: add an
assertion that the emitted SSE ID equals content.len() to verify the exact
resume boundary is calculated correctly, and add an assertion that the resumed
stream data starts exactly with "from job" to ensure the handler seeks to the
precise offset rather than one byte early or late.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: acee9f77-e335-424c-98ab-e62f3d893f7e
📒 Files selected for processing (1)
src/dashboard/server.rs
CodeRabbit review: the prior contains/does-not-contain assertions would still pass with a missing id or an off-by-one seek. Assert the emitted SSE id equals the exact byte offset and that resumed data starts precisely at "from job", not one byte early or late.
…m update (#496) * fix(mcp): resolve sequence_ids in comment and honour parent_id on item update comment(create|list) passed item_id straight to the backend, so a bare or #-prefixed sequence_id reached the INSERT and came back as a raw "FOREIGN KEY constraint failed". It was the one id-taking tool that never called resolve_item_id. Both arms now resolve, via a new resolve_existing_item_id that also proves the item exists so an unknown UUID fails as invalid_params naming the id instead of leaking SQLite constraint text. item(update) accepted parent_id and silently discarded it — UpdateItem had no such field and the update path never read req.parent_id, so the response echoed the unchanged parent with no error. UpdateItem now carries parent_id: Option<Option<String>> (None leaves it alone, Some(None) detaches, Some(Some(id)) re-parents) and the MCP layer resolves a sequence_id or UUID the same way it resolves id. Because update is the only path that can close a parent cycle, the backend rejects a self-parent or a parent that is already a descendant, bounded so data that is already cyclic errors instead of looping forever. The comment tests move to their own tests/comment_tests.rs to keep action_tests.rs under the LOC gate. Closes #375, closes #377. Agentflare-Agent: claude-code Agentflare-Branch: task/375-comment-mcp-tool-rejects-numeric-sequenc Agentflare-Item: 375 * fix(mcp): resolve sequence_id parent_id on item create, not just update item(create)'s parent_id was passed straight through to the INSERT's FK column unresolved, so a sequence_id or #-prefixed id (which the schema docstring already advertises as accepted) surfaced a raw "FOREIGN KEY constraint failed" instead of the named invalid_params error update's parent_id already gets. Reuse resolve_existing_item_id the same way item_update does. Agentflare-Agent: claude-code Agentflare-Branch: task/375-comment-mcp-tool-rejects-numeric-sequenc Agentflare-Item: 375 --------- Co-authored-by: shiva <shiva@gosysinfo.tech>
Summary
jobs.html) streams the full log again from the start on every SSE reconnect instead of just resuming — looks like the log is replaying in a loop.GET /api/jobs/:id/streamnow tags each SSE event with the cumulative byte offset as itsid:, and honors an incomingLast-Event-IDheader as the starting offset. Browsers auto-reconnect a droppedEventSourceand resend that header, so a reconnect (idle proxy timeout, tab suspend, brief network blip) now only receives bytes past what was already delivered instead of re-tailing from byte 0.EventSourcesendsLast-Event-IDnatively.Test plan
cargo test -p agentflare --bin agentflare -- dashboard::server::tests::jobs_— 8/8 pass, including newjobs_stream_endpoint_resumes_from_last_event_idregression testcargo check/cargo clippyclean onsrc/dashboard/server.rsSummary by CodeRabbit