Skip to content

serving event streams

nate
Jul 9, 20263 min read1 read

serving event streams

he emitting side of the firehose — what a PDS or jetstream-class service does so that consumers get a resumable, ordered stream. evidence from two zig implementations: zds, a PDS (the subscribeRepos emitter), and stream, a reimplementation of bluesky's jetstream (the JSON-simplified firehose mirror) — which inherited twelve incident-derived rules from zlay, a relay (docs/lessons-from-zlay.md).

precompute frames at commit time

zds builds each event frame once, inside the write transaction, and stores it verbatim in seq_events(seq, did, commit_cid, evt) (store.zig:2008). subscribeRepos streams stored bytes; it never re-encodes on the hot path, and a replay from cursor N serves exactly the bytes a live subscriber saw.

durability before emission

never broadcast a seq a subscriber could try to replay before it's durable. the working shape is an atomic committed_seq watermark that emission reads — zlay's alternative (broadcasting from inside the flush) "serialized 2,670 producers and OOM'd" (stream/docs/lessons-from-zlay.md #3, stream/archive.zig).

fan out by letting readers pull

stream keeps one bounded deque of encode-once frames; each subscriber thread reads at its own index and blocks on a condvar at the tip (tail.zig:1-9). there are no per-subscriber queues, so a slow reader costs nothing but its own lag — backpressure can't propagate from one consumer to the ingest path or to other consumers. filters apply at read time and swap atomically on options_update (server.zig:63-77).

the cursor contract has sharp edges

jetstream v1's /subscribe cursor semantics, as actually implemented (stream/docs/upstream-subscribe-protocol.md, server.zig:104-113, tail.zig:98-108):

  • absent → live tail; malformed or negative → HTTP 400 before the upgrade
  • future cursor → silently live; cursor 0 → floored to the first event
  • too-old cursor → silently clamped to the oldest retained event — a consumer that was offline past the retention window resumes with a gap and no error
  • replay is inclusive-overlapping (first event with witnessed_at >= cursor); clients are expected to dedupe

a durable upstream cursor needs the same honesty: stream fsyncs its cursor on a 5s interval and documents the consequence plainly — "at-least-once on restart within the flush window" (cursor.zig:6-10).

drop, count, never crash — and never log per-event

hostile or malformed input is table stakes on a public stream. stream's validation gate drops a bad op (or event) and increments a per-reason counter (dropped_events_total{reason=...}), with no log line — "hostile input must not drive log volume" (convert.zig, docs/upstream-harness.md:29). relatedly: a create whose record block is missing from the CAR is spec-legal — drop that op, keep the event.

count what your checks do

verification counters exist to prove verification is verifying — "a check that silently passes everything is indistinguishable from a working one without them" (stream/metrics.zig:4-7). same family: a build_info{git_sha} metric proves which binary is even running.

sources

  • zdssrc/atproto/store.zig (seq_events), src/http/sync.zig (hand-rolled websocket write path)
  • streamtail.zig, server.zig, convert.zig, cursor.zig, docs/upstream-subscribe-protocol.md, docs/lessons-from-zlay.md
  • event stream spec

Did you enjoy this article?

Recommend it — Standard Reader surfaces well-loved writing to more readers across the network.

Across the AtmosphereDiscussions