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

Class Method Summary collapse

Class Method Details

.record_update(path, bytes, now: monotonic) ⇒ Object



30
31
32
33
34
35
36
# File 'app/services/mbeditor/collaboration_doc_store.rb', line 30

def record_update(path, bytes, now: monotonic)
  MUTEX.synchronize do
    room = touch(path, now)
    room[:deltas] << bytes
  end
  nil
end

.replace_snapshot(path, bytes, now: monotonic) ⇒ Object



38
39
40
41
42
43
44
45
# File 'app/services/mbeditor/collaboration_doc_store.rb', line 38

def replace_snapshot(path, bytes, now: monotonic)
  MUTEX.synchronize do
    room = touch(path, now)
    room[:snapshot] = bytes
    room[:deltas] = []
  end
  nil
end

.reset!Object



66
67
68
69
70
71
72
# File 'app/services/mbeditor/collaboration_doc_store.rb', line 66

def reset!
  MUTEX.synchronize do
    @rooms = {}
    @last_sweep = nil
  end
  nil
end

.state_for(path, now: monotonic) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'app/services/mbeditor/collaboration_doc_store.rb', line 47

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



61
62
63
64
# File 'app/services/mbeditor/collaboration_doc_store.rb', line 61

def sweep!(now: monotonic, grace: GRACE_TTL)
  MUTEX.synchronize { evict_idle(now, grace) }
  nil
end