Module: Mbeditor::CollaborationDocStore
- Defined in:
- app/services/mbeditor/collaboration_doc_store.rb
Overview
Thread-safe, in-memory cache of opaque Yjs bytes per active file. It never interprets the bytes (no Ruby CRDT): it only buffers the latest snapshot plus recent deltas so a late-joiner can sync instantly. Modeled on the existing TTL caches (GitInfoService, AvailabilityProbe): Mutex + monotonic clock.
Constant Summary collapse
- GRACE_TTL =
Grace window (seconds): a room with no activity for longer than this is evicted by sweep!. Because every op refreshes last_activity, a room that just went empty survives this long, so a quick reopen recovers its buffer.
300- ROOM_CAP =
Hard cap on cached rooms. Exceeding it evicts the least-recently-active room, so process memory stays bounded even if rooms are never swept.
200- SWEEP_INTERVAL =
Idle GC rides on traffic: every write/read attempts a sweep, but the scan runs at most once per this interval so a busy room doesn't pay for it on every op. With no explicit scheduler, memory stays bounded over a long session as long as some activity continues. Kept well under GRACE_TTL so an idle room is reclaimed soon after its grace window elapses.
60- MAX_DELTAS =
Hard cap on buffered deltas per room; a long editing session on one file would otherwise grow it without limit. Over the cap the oldest go, which costs a late joiner an incomplete replay — it then waits for the next snapshot instead of syncing from the buffer.
500
Class Method Summary collapse
- .record_update(path, bytes, now: monotonic) ⇒ Object
- .replace_snapshot(path, bytes, now: monotonic) ⇒ Object
- .reset! ⇒ Object
- .state_for(path, now: monotonic) ⇒ Object
- .sweep!(now: monotonic, grace: GRACE_TTL) ⇒ Object
Class Method Details
.record_update(path, bytes, now: monotonic) ⇒ Object
36 37 38 39 40 41 42 43 |
# File 'app/services/mbeditor/collaboration_doc_store.rb', line 36 def record_update(path, bytes, now: monotonic) MUTEX.synchronize do room = touch(path, now) room[:deltas] << bytes room[:deltas].shift while room[:deltas].size > MAX_DELTAS end nil end |
.replace_snapshot(path, bytes, now: monotonic) ⇒ Object
45 46 47 48 49 50 51 52 |
# File 'app/services/mbeditor/collaboration_doc_store.rb', line 45 def replace_snapshot(path, bytes, now: monotonic) MUTEX.synchronize do room = touch(path, now) room[:snapshot] = bytes room[:deltas] = [] end nil end |
.reset! ⇒ Object
73 74 75 76 77 78 79 |
# File 'app/services/mbeditor/collaboration_doc_store.rb', line 73 def reset! MUTEX.synchronize do @rooms = {} @last_sweep = nil end nil end |
.state_for(path, now: monotonic) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'app/services/mbeditor/collaboration_doc_store.rb', line 54 def state_for(path, now: monotonic) MUTEX.synchronize do room = rooms[path] if room room[:last_activity] = now result = { snapshot: room[:snapshot], deltas: room[:deltas].dup } else result = { snapshot: nil, deltas: [] } end maybe_sweep(now) result end end |
.sweep!(now: monotonic, grace: GRACE_TTL) ⇒ Object
68 69 70 71 |
# File 'app/services/mbeditor/collaboration_doc_store.rb', line 68 def sweep!(now: monotonic, grace: GRACE_TTL) MUTEX.synchronize { evict_idle(now, grace) } nil end |