System Design Cases
Real-time Collaboration Architecture
Real-time collaboration architecture from frontend perspective: WebSocket gateway with sticky sessions by docId, stateful Yjs doc actor tier handling CRDT merge, separate Redis pub-sub for ephemeral awareness/cursors, IndexedDB-backed offline ops queue, snapshot persistence to Postgres+S3. Four scenarios: presence cursor broadcast (15 Hz throttle + interpolation), CRDT concurrent merge (commutative ops, no transform), offline reconnect with vector-clock catchup, multi-cursor with server-side ACL denial and client rollback. Includes ADR comparing Liveblocks managed vs Yjs+Hocuspocus self-host vs PartyKit edge.
Realtime collaboration: CRDT convergence, durable ack и presence isolation
WebSocket даёт двусторонний transport, но не persistent log, authorization model или convergence. Collaborative system отдельно определяет document datatype, durable acknowledgement, reconnect protocol, presence lifetime, access revocation и compaction frontier.
CRDT может гарантировать deterministic convergence при выполнении своих delivery/merge assumptions. Это не гарантирует business invariants, semantic quality или мгновенную синхронизацию; disconnected replicas могут оставаться разными, пока не получат одинаковые updates.
Проверяемые утверждения
- Yjs document updates commutative, associative и idempotent: порядок и повторная доставка не меняют итог после применения одинакового множества updates.
- Awareness/presence — ephemeral state и не должна сохраняться как document history. Offline client удаляется по timeout и позже публикует новый presence state.
- Acknowledgement означает только тот durability point, который система явно реализовала. Для recoverable collaboration сервер подтверждает после durable log/replication policy, не сразу после socket receive.
- GC/tombstone compaction требует знать, какие offline actors больше не принесут старую историю, либо принудить их к full resync from a retained snapshot.
Границы и компоненты
| Компонент | Роль в модели |
|---|---|
| Editor A | WebSocket session |
| Editor B | WebSocket session |
| Authenticated Realtime Gateway | WebSocket session; WebSocket session; authorize document operation; validated update; consume accepted updates |
| Document Authorization | authorize document operation |
| CRDT Document Room | validated update; append before durable ack; load and checkpoint; publish accepted update; ephemeral presence |
| Durable Update Log | append before durable ack; truncate safe prefix |
| Versioned Snapshot Store | load and checkpoint; write verified snapshot |
| Ordered Room Fan-out | publish accepted update; consume accepted updates |
| Ephemeral Awareness | ephemeral presence |
| Safe-frontier Compactor | truncate safe prefix; write verified snapshot |
Сценарии
Sync concurrent document edits
Two authorized editors create concurrent CRDT updates. The room validates size/schema, appends accepted updates durably, applies them and fans them out; duplicates remain harmless at the datatype layer.
Проверяемый исход: After both replicas receive the same accepted updates they converge deterministically; presence remains separate from document history.
Reconnect from a state vector
Editor A reconnects with a state vector rather than assuming socket continuity. The room loads a snapshot/log frontier, computes the missing update and returns it; client updates can be retried idempotently.
Проверяемый исход: Reconnect repairs missed messages without full history when retained state permits; an unsupported old frontier receives a full snapshot resync.
Expire ephemeral awareness separately
Editor A publishes cursor/presence through the authenticated room. The awareness channel removes that state after disconnect timeout without appending a document deletion or changing authored content.
Проверяемый исход: Presence can disappear and later be republished independently while the durable document remains unchanged.
Revoke access and require resynchronization
Document access is revoked while a socket is open. Gateway rechecks authorization, closes the room subscription and rejects later updates; previously downloaded content cannot be remotely erased by protocol promise.
Проверяемый исход: Future reads/writes stop promptly, audit records the revocation, and re-grant requires a new authorized sync rather than trusting the old socket.
Compact only behind a safe frontier
The compactor writes and verifies a snapshot at a durable log sequence. It truncates only a prefix no supported offline actor needs, or marks older clients for mandatory full snapshot resync.
Проверяемый исход: Compaction bounds retained history without invalidating the convergence/reconnect contract for supported clients.
Failure, concurrency и evolution checklist
- Reconnect with application-level document/version state; a new WebSocket connection has no memory of missed application messages.
- Make accepted update IDs idempotent through append/apply/fan-out. Handle crash after append but before acknowledgement as retry, not a new edit.
- Backpressure or disconnect slow clients before unbounded per-socket buffers exhaust the gateway. Resume through durable replay/state-vector sync.
- Separate ephemeral awareness timeout from durable document retention; cursor disappearance must not delete authored content.
Security и privacy
- Authenticate the handshake and authorize every document subscription/update. For browser clients validate expected Origin; Origin is not authentication for non-browser clients.
- Limit message/frame/reassembled size, update complexity, rooms per session and rate. RFC 6455 requires implementations to protect against implementation-specific limits.
- TLS protects transport, not a compromised server or already downloaded content. Make any end-to-end encryption claim explicit about keys, metadata and server-side validation limitations.
- Revocation stops future access but cannot prove deletion from a client that already received plaintext.
Метрики, формулы и допущения
- Without hierarchical fan-out, approximate egress messages per accepted update are
active_recipients − 1;egress_bytes ≈ update_bytes × recipients + protocol_overhead. replay_bytes = retained_missing_updates_bytesor one snapshot plus delta; choose the smaller only if both represent the same verified document frontier.room_memory ≈ active_document_state + awareness_state + bounded_socket_buffers + indexes; durable log bytes are storage, not room RAM.- Convergence time is a distribution of propagation/reconnect delays. CRDT algebra does not provide a fixed latency bound.
Числа и bounds выше действуют только при названных units, population и assumptions. Ни паттерн, ни browser API сами по себе не задают SLA, capacity или correctness.
Решения для production review
- Choose OT/CRDT/locking from editing semantics and invariants, then document the exact convergence and conflict behavior.
- Define what an acknowledgement proves: gateway receive, quorum append or durable replicated commit.
- Specify offline lease/retention and full-resync policy before enabling compaction or tombstone GC.
Первичные и официальные источники
- https://docs.yjs.dev/api/document-updates
- https://docs.yjs.dev/getting-started/adding-awareness
- https://www.rfc-editor.org/rfc/rfc6455.html
- https://inria.hal.science/inria-00555588
Scope note
Диаграмма показывает Yjs-like update algebra поверх durable service. Она не доказывает Byzantine safety, linearizability, business-invariant preservation, guaranteed delivery, end-to-end encryption или unlimited offline retention.