Context
The CRDT op log is currently in-memory only on the collab server (docOpLog in CanvasWebSocketHandler). Server restart = all CRDT history lost. Reconnecting clients get stale or empty state. Sim uses durable server-side snapshots + op logs for crash recovery and reconnection.
What to build
Server-side: Periodic snapshots
- Persist full canvas snapshots to PostgreSQL at regular intervals (e.g., every 30s of activity, or on significant structural changes)
- Snapshot = serialized state of all nodes + edges + CRDT document states
- Keep last N snapshots for rollback capability
Durable CRDT op log
- Move
docOpLog from in-memory Map<String, List<JsonNode>> to PostgreSQL
- Move
docSeenOpKeys dedup set to DB (or derive from op log)
- Index by
(canvasId, docId, seq) for efficient range queries
Reconnection protocol
- Client sends
sync_request with its stateVector (already implemented)
- Server responds with ops since that vector from durable storage (not in-memory)
- If gap is too large (client too far behind), send full snapshot instead
Crash recovery
- On server restart, load active canvas state from latest snapshot + pending ops from durable log
- Resume serving without data loss
Current code references
services/collab/src/main/java/com/leetdoodle/collab/handler/CanvasWebSocketHandler.java — in-memory docOpLog, docSeenOpKeys, handleSyncRequest()
frontend/src/shared/crdt/document.ts — getStateVector(), getOpsSince() (client-side sync protocol already exists)
frontend/src/shared/crdt/useCanvasCrdt.ts — sync request hook
Acceptance criteria
Context
The CRDT op log is currently in-memory only on the collab server (
docOpLoginCanvasWebSocketHandler). Server restart = all CRDT history lost. Reconnecting clients get stale or empty state. Sim uses durable server-side snapshots + op logs for crash recovery and reconnection.What to build
Server-side: Periodic snapshots
Durable CRDT op log
docOpLogfrom in-memoryMap<String, List<JsonNode>>to PostgreSQLdocSeenOpKeysdedup set to DB (or derive from op log)(canvasId, docId, seq)for efficient range queriesReconnection protocol
sync_requestwith itsstateVector(already implemented)Crash recovery
Current code references
services/collab/src/main/java/com/leetdoodle/collab/handler/CanvasWebSocketHandler.java— in-memorydocOpLog,docSeenOpKeys,handleSyncRequest()frontend/src/shared/crdt/document.ts—getStateVector(),getOpsSince()(client-side sync protocol already exists)frontend/src/shared/crdt/useCanvasCrdt.ts— sync request hookAcceptance criteria