Reconnect & replay
When a client loses its connection, optimistic writes don’t stop — they queue in an in-memory buffer and replay when the socket returns. This page is the exact contract for that replay: how re-sends are de-duplicated, how long the guarantee holds, which writes are exempt, and how a still-live-but-lossy link recovers a single dropped frame.
By default the buffer is in-memory; with the opt-in persisted write queue it survives a page reload too, and the reloaded client replays it under exactly the rules on this page — a reload is a reconnect, with no separate cold-start semantics.
On reconnect the two lanes reconcile differently: structured (JSON Patch) writes replay last-writer-wins, while the Yjs lane self-heals by CRDT merge — deltas merge cleanly no matter how late they arrive.
Re-sends are de-duplicated, within a bounded window
Section titled “Re-sends are de-duplicated, within a bounded window”Replay is safe to repeat: the server records each write’s client event id and re-acks a duplicate instead of applying it twice, so a write that committed but whose acknowledgement was lost can be re-sent. This covers every replayable write kind — updates, creates, deletes, restores and renames — so a replay also never re-applies over what happened in between: a delete replayed after someone restored the document re-acks with its live state instead of re-deleting it, a replayed restore doesn’t resurrect a document deleted since, and a replayed rename never clobbers a newer name. That dedup record is retained only for a bounded window.
A write that was sent and then sat unconfirmed longer than a client-side
horizon (30 minutes, half the server’s retention) is dropped rather than
replayed — but not silently. The optimistic entry rolls back to server truth and
the outcome is reported as unconfirmed: an awaited write rejects, a
fire-and-forget one hits onWriteError, and the caller refetches. That beats the
two silent alternatives — a write that vanishes without a trace, or a replay that
risks a double-apply once the dedup record has aged out.
Writes made purely offline never reached the server, so they carry no such risk and always replay, however long you were away.
When a write keeps failing with a transient storage error the client retries it a few times; during a systemic outage a circuit breaker stops piling on retries once many writes are failing at once.
Sequence-guarded writes are exempt
Section titled “Sequence-guarded writes are exempt”Sequence-guarded writes replay at any age: their compare-and-set base means a late replay of a write that already committed is rejected benignly rather than applied twice, so they replay instead of being surfaced. The exception is when later unguarded work was stacked on top of one — an unguarded edit, or a rename, delete or restore of the same document — since all of it was authored assuming the CAS applied, so the whole chain is surfaced instead.
Lossy-but-live links get swept too
Section titled “Lossy-but-live links get swept too”De-duplicated re-sending isn’t only for reconnects. A lossy but live link (patchy Wi-Fi, a flaky proxy) can drop a single frame without killing the socket — heartbeats still flow, so nothing reconnects and nothing replays. The client therefore sweeps its own buffer: an update still unconfirmed after a bound (10 seconds by default) is re-sent under its original event id, and keeps being re-sent each period until the server settles it. If the original write actually committed and only its acknowledgement was the lost frame, the re-send draws a dedup re-ack instead of a second apply, and the client resyncs the document to fetch the data that acknowledgement would have carried. The same 30-minute horizon bounds this path too.