You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Task event SSE subscriptions had no terminal condition: stream_task_events ran a
`while True` loop that only exited on client disconnect or a fatal error. Once a
task finished, its producer stopped writing but the reader kept blocking on the
Redis stream (XREAD) forever, pinning a connection from the shared per-process
pool. Because stream keys carry a sliding TTL, a finished task's key eventually
expires while readers keep blocking on it — a permanent zombie. Accumulated
zombies exhaust the pool, which is shared with the readiness probe, so /readyz
fails while the dependency-free /healthz stays 200: the pod goes Unready and is
never restarted.
Termination now has three independent, deterministic checks:
- Connect-time: read task status once at connect (after snapshotting the cursor,
so a racing terminal event still lands after the cursor). If already terminal,
replay buffered events and end — handles late connects.
- In-stream: end on a terminal task_updated event (the last event a task emits).
- Periodic authoritative recheck: at the top of every loop iteration, on an
interval, re-read task status and end if terminal. Runs busy or idle and even
after a read failure/backoff, so a dropped/lost terminal event or a failing
read cannot keep the stream open. A hard-deleted task (ItemDoesNotExist) is
treated as terminal; other lookup errors fall through to transient retry.
Also:
- AgentTaskService.fail_task now publishes task_updated via update_task — it was
the only terminal write that didn't emit, which could strand a live viewer.
- Drop the per-subscriber cleanup_stream in `finally`: the topic is shared by all
viewers, so deleting it on one exit broke the others. The sliding TTL reclaims.
- Canonical NON_TERMINAL/TERMINAL task-status sets on the TaskStatus entity,
referenced by both the status state machine and SSE termination.
Adds integration regression tests: event-driven termination, late-connect
termination, a running task surviving a reclaimed key, and shared-stream
survival on disconnect.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0 commit comments