Context
The system supports 10-20 concurrent editors on the same work. These optimizations would improve network efficiency, reduce latency, and extend capacity to 50-100+ concurrent editors.
Current State
Optimizations (in priority order)
1. Server-side delta batching (Medium effort, High impact)
Problem: Each character typed generates a crdt_sync_update that is relayed individually to all other sessions. A 50-character paste = 50 messages per recipient.
Solution: Batch deltas on the server before relaying:
- Accumulate deltas for 50-100ms per work
- Merge consecutive operations (Retain(1)x10 to Retain(10))
- Send one batched message per recipient per interval
- Falls back to immediate relay for single-character edits (low overhead)
Impact: Reduces WebSocket message count by 5-10x during rapid typing. At 20 users, a paste sends 20 messages instead of 1000.
2. Sequence-based reconnect (Medium effort, High impact)
Problem: On reconnect, client receives full CRDT state even if it only missed a few updates.
Solution: Track update sequence numbers:
- Each
crdt_sync_update carries a monotonic sequence number per work
- Client stores its last-applied sequence
- On reconnect, client sends
{work_id, last_seq}
- Server sends only updates after
last_seq
- If
last_seq is too old (>1000 updates behind), fall back to full state
Impact: Reconnection is instant instead of multi-second. Critical for flaky networks and mobile.
3. Periodic CRDT snapshots (Medium effort, Medium impact)
Problem: Replay-based catch-up is O(n) in number of missed updates.
Solution: Server periodically creates compressed snapshots:
- Every 30s or every 100 edits per work
- On reconnect, client gets latest snapshot + subsequent deltas
- Snapshots are memory-efficient (compressed CRDT state)
Impact: Catch-up time is O(log n) instead of O(n). Matters for works with long edit histories.
4. Delta compression (Low effort, Medium impact)
Problem: Deltas can contain redundant operations, especially after merging.
Solution: Compress deltas before relaying:
- Merge consecutive Retain operations
- Merge consecutive Insert operations (same author, adjacent position)
- Drop no-op operations (Retain(0), Delete(0))
- Optionally gzip large deltas (>1KB)
Impact: 2-3x reduction in delta payload size. No protocol changes needed.
5. Lazy CRDT materialization (Low effort, Medium impact)
Problem: Every 5 seconds, ALL pending CRDT docs are materialized to work revisions.
Solution: Smarter scheduling:
- Only materialize docs with pending changes AND modified since last materialization
- Add max-pending-threshold: force-materialize if >500 pending changes
- Already partially implemented via
pending_work_ids()
6. Binary protocol for CRDT updates (Large effort, High impact at scale)
Problem: CRDT updates use JSON encoding, which is verbose.
Solution: Use postcard (binary) encoding for CRDT-specific messages:
- Delta operations as fixed-width integers instead of JSON strings
- Awareness updates as binary structs
- Keep JSON for non-performance-critical ops
Impact: 3-5x smaller payloads at high concurrency.
7. Per-work write lock sharding (XL effort, High impact at scale)
Problem: All edits go through one RwLock. The write lock serializes all mutations.
Solution: Shard the write lock per-work. Edits to different works don't block each other.
Maps to issue #18 (RwLock or Sharded Concurrency).
Priority
| # |
Optimization |
Effort |
Impact |
| 1 |
Delta batching |
M |
High |
| 2 |
Sequence reconnect |
M |
High |
| 3 |
CRDT snapshots |
M |
Medium |
| 4 |
Delta compression |
L |
Medium |
| 5 |
Lazy materialization |
L |
Medium |
| 6 |
Binary protocol |
XL |
High |
| 7 |
Write lock sharding |
XL |
High |
Related
Context
The system supports 10-20 concurrent editors on the same work. These optimizations would improve network efficiency, reduce latency, and extend capacity to 50-100+ concurrent editors.
Current State
Optimizations (in priority order)
1. Server-side delta batching (Medium effort, High impact)
Problem: Each character typed generates a
crdt_sync_updatethat is relayed individually to all other sessions. A 50-character paste = 50 messages per recipient.Solution: Batch deltas on the server before relaying:
Impact: Reduces WebSocket message count by 5-10x during rapid typing. At 20 users, a paste sends 20 messages instead of 1000.
2. Sequence-based reconnect (Medium effort, High impact)
Problem: On reconnect, client receives full CRDT state even if it only missed a few updates.
Solution: Track update sequence numbers:
crdt_sync_updatecarries a monotonic sequence number per work{work_id, last_seq}last_seqlast_seqis too old (>1000 updates behind), fall back to full stateImpact: Reconnection is instant instead of multi-second. Critical for flaky networks and mobile.
3. Periodic CRDT snapshots (Medium effort, Medium impact)
Problem: Replay-based catch-up is O(n) in number of missed updates.
Solution: Server periodically creates compressed snapshots:
Impact: Catch-up time is O(log n) instead of O(n). Matters for works with long edit histories.
4. Delta compression (Low effort, Medium impact)
Problem: Deltas can contain redundant operations, especially after merging.
Solution: Compress deltas before relaying:
Impact: 2-3x reduction in delta payload size. No protocol changes needed.
5. Lazy CRDT materialization (Low effort, Medium impact)
Problem: Every 5 seconds, ALL pending CRDT docs are materialized to work revisions.
Solution: Smarter scheduling:
pending_work_ids()6. Binary protocol for CRDT updates (Large effort, High impact at scale)
Problem: CRDT updates use JSON encoding, which is verbose.
Solution: Use postcard (binary) encoding for CRDT-specific messages:
Impact: 3-5x smaller payloads at high concurrency.
7. Per-work write lock sharding (XL effort, High impact at scale)
Problem: All edits go through one RwLock. The write lock serializes all mutations.
Solution: Shard the write lock per-work. Edits to different works don't block each other.
Maps to issue #18 (RwLock or Sharded Concurrency).
Priority
Related