Engineering note · 2 of 6
Stream and wait handlers subscribe to the event broker, then enqueue the job. Reverse that order and a fast runner can publish tokens into a void — the client never sees the opening of the answer.
Creating a run and streaming it back look like one HTTP request, but three processes are involved: the control-plane replica holding the SSE connection, Redis (or NATS) as the event log, and a runner that may start in milliseconds.
enqueue first LPUSH queue → runner starts → XADD token#1 Subscribe… ← too late · token#1 already past the tail subscribe first Subscribe → XREAD waiting on this run’s stream LPUSH queue → runner starts → XADD token#1 SSE sees #1 · no gap
For POST …/runs/stream and …/runs/wait the handler
order is fixed:
broker.Subscribe(runID) — returns a Go
channel; a background reader tails the stream from “now.”
enqueue(assignment) — only then may a
runner dequeue and publish.
Background POST …/runs skips subscribe on purpose — the client
asked for fire-and-forget. Replay still works later because Redis Streams keep
events (~24h TTL).
Pub/sub is fire-and-forget: miss the window and the message is gone. Streams are an ordered log with offsets, so Subscribe can block on new entries and Replay can catch up after a reconnect. The broker interface hides that — in-process mode uses Go channels; multi-replica uses XADD/XREAD.
Ordering is the product: subscribe → enqueue → stream. One reversed line and your “streaming” API silently drops the first tokens under load.