Expand description
com.atproto.sync.subscribeRepos: this server’s repositories, as atproto
wants to hear about them.
A WebSocket of framed DAG-CBOR carrying signed commits and the blocks that prove them, which is what a relay crawls.
§The frame
One WebSocket binary message is two concatenated DAG-CBOR values: a header
and a body. The header is {"op": 1, "t": "#commit"} for a message, and
{"op": -1} for an error, whose body is {"error": .., "message": ..}.
Nothing separates the two values; a reader decodes the header and continues
from where that decode stopped.
§Sequence numbers and cursors
Every message frame carries a sequence number, and a consumer hands back
the last one it applied as ?cursor=. The cursor is a bare integer with
nowhere to name the run of the server that minted it, so the number
itself survives a restart — see
didbot_pds::sequence for how, and for why a restart skips numbers
rather than repeating them.
The replay buffer holds at most DEFAULT_REPLAY_FRAMES frames and
DEFAULT_REPLAY_BYTES of them, evicting the oldest first, unless a
deployment sets its own ReplayBounds. It lives in memory, so a restart
empties it even though the numbering survives. A cursor the buffer cannot
reach is answered with #info OutdatedCursor and
then everything still held: the consumer has a hole it must fill by reading the
repositories, and being told so is the point. A cursor naming a number
this server has not assigned yet is a different thing — it cannot be
filled in from anywhere — so it is the FutureCursor error the lexicon
declares, and the connection closes.
A boot numbers frames before anything can connect: the positions that
changed while the process was down, and a restore’s resets. A producer
built with Repos::booting keeps every one of them until
Repos::listening, and its bounds’ worth more on top, so a relay that
connects after the boot is handed all of them.
§One copy of each frame
The replay buffer is the only place a frame is held. A consumer keeps its position in the buffer and reads each frame from there when it is ready to send it, the replay and the live stream alike.
A frame an open stream has not sent yet is kept past the bounds, up to
twice them, so a consumer resuming from the oldest frame held has as much
room to fall behind as one following live. Past twice the bounds the
oldest frames go anyway, and a consumer that had not sent one is ended
with ConsumerTooSlow: its next frame is gone. So the firehose’s memory
is at most twice the bounds, plus the one frame each connection is
writing to its socket.
§Only what is durable
A record write is in the kernel when it returns and on the medium within
DEFAULT_SYNC_INTERVAL. A #commit published in between announces a
commit a power cut can take back, and a relay that applied it holds a
head this server no longer has. So a producer over a write-ahead log holds
each commit, beside the position the log had reached when it was handed
over, and publishes it once the log has synced through that position. A
commit’s frame is therefore up to one sync interval later than its write.
Every other frame syncs the log and publishes the commits held before it
first, so the stream keeps the order the writes made: an erasure’s
#account after the repository’s last #commit, and a restore’s #sync
after the commit it carries is on the medium.
Structs§
- Exclusions
- Which message types one connection wants.
- Frame
- One numbered frame, already encoded.
- Held 🔒
- Commits handed over before the log they are written to has synced past them. See the module docs.
- Inner 🔒
- Replay
Bounds - How much the replay buffer holds. It evicts its oldest frame when a new one would take it past either bound, unless an open stream has not sent that frame yet. Those it keeps up to twice the bounds. See the module docs.
- Repos
- The producer: the sequence and the replay buffer consumers read from.
- Repos
Stream - One consumer’s stream: the replay, then whatever arrives live, both read from the replay buffer.
- Ring 🔒
- The replay buffer’s frames, and how much it holds.
- Start 🔒
- Where one consumer starts reading the replay buffer, decided in
Repos::start_for.
Enums§
- Frame
Kind - Which of the lexicon’s message types a frame is.
- Read 🔒
- What one look at the buffer found for a stream.
- Resume 🔒
- What a connection asked for.
Constants§
- DEFAULT_
REPLAY_ BYTES - How many encoded bytes the replay buffer holds, if a deployment does not choose.
- DEFAULT_
REPLAY_ FRAMES - How many frames the replay buffer holds, if a deployment does not choose.
- FUTURE_
CURSOR - The lexicon’s error for a cursor ahead of the stream.
- OP_
ERROR 🔒 - The
opan error frame’s header carries. - OP_
MESSAGE 🔒 - The
opa message frame’s header carries. - OUTDATED_
CURSOR - The one
namethis server ever sends on an#infoframe.
Functions§
- account_
body 🔒 - The
#accountbody. - clamp 🔒
- The lexicon’s
seq, which is a signed integer where this counter is not. - commit_
body 🔒 - The
#commitbody, as the lexicon defines it. - encode_
error - Encodes one error frame, which closes the connection after it.
- encode_
message - Encodes one message frame: the header, then the body.
- identity_
body 🔒 - The
#identitybody. - info_
body 🔒 - The
#infobody. - link 🔒
- A CID-link, or null for the fields the lexicon marks nullable.
- map 🔒
- A DAG-CBOR map from pairs, so the bodies below read as their lexicons.
- now_
rfc3339 🔒 - The current time, RFC 3339, seconds precision.
- publish_
as_ 🔒synced - Publishes held commits as the log syncs past them, on a thread of its own.
- sync_
body 🔒 - The
#syncbody.