Expand description
Room-keyed realtime primitive (Phase 1 of the multi-user collaboration epic).
This module is the transport-agnostic fan-out core that chat fan-out,
CRDT doc-sync (Phase 3), and presence/awareness all consume. It is a sibling
to Core’s identity_verify (the USER-identity layer) and intentionally
knows nothing about WebSockets, JWTs, or access control — those live in the
WS handler (stage 2/3) that drives this registry.
§Shape
- A
RoomRegistrymapsroom_id-> aRoomHandle. Each live room runs as ONE tokio actor task ([run_room]) that owns the room’s ephemeral state (presence map + idle clock) behind a command channel, plus atokio::sync::broadcastsender for fan-out to every joined member. - Membership is reference-counted via an
AtomicUsizeshared between the handle and the actor.RoomHandle::joinreturns aRoomMembershipRAII guard whoseDropdecrements the count, evicts the member’s presence, and broadcasts apresence_leavedelta — so a client that drops its socket without a clean leave is still reaped. - Hibernation is the single biggest scaling lever: a room that has had
zero members for longer than
RoomConfig::idle_windowexits its actor and is removed from the registry (rehydrated on the next join). Evictions are logged.
§Race safety (membership vs eviction)
Concurrent callers MUST join via RoomRegistry::join, whose get-or-create
and fetch_add both run while holding the registry Mutex. The actor’s
eviction recheck ([try_evict]) takes that same lock, so the two serialize:
either join wins (eviction then sees members > 0 and skips) or eviction wins
(removes the entry and exits; join transparently re-creates a fresh room).
There is no window in which a caller ends up holding a handle to a room the
registry has dropped.
The lower-level RoomRegistry::get_or_create + RoomHandle::join two-step
is NOT race-safe against eviction (the increment happens outside the lock) and
exists only for single-threaded tests with controlled lifecycles.
§Channels
A Frame carries a RealtimeChannel tag. Events and Presence carry
serde_json::Value (JSON text on the wire); DocSync carries opaque
Vec<u8> that passes through untouched (reserved for Phase 3 — accept and
relay binary without interpreting it).
Presence is NEVER persisted: it lives only in the actor’s in-memory map with a heartbeat TTL, and vanishes when the room hibernates.
Staging note: stage 1 builds the primitive with unit tests. Wiring into
ServerState, the GET /api/realtime/ws route, and append_message fan-out
happens in stages 2/3, so several items are intentionally unused for now.
Structs§
- ConnId
- Opaque, process-unique identity for one subscriber
Connection. The address a targetedRoomHandle::send_eventdelivers to. - Connection
- A typed subscriber to one room, addressable by its
ConnId. It merges the room’s broadcast fan-out with eventsRoomHandle::send_eventdelivers to it privately, decoding each into a typedEvent. Dropping it unregisters the targeted channel from the room actor — the RAII unsubscribe handle, the same pattern Rivet’sactor.on(...)teardown gives you. - Event
- A decoded typed room event: a name plus its JSON payload. Produced by
Connection::recvfrom an envelopedFrame::Event. - Room
Config - Tunables for room lifecycle.
RoomConfig::defaultuses production values; tests construct short windows viaRoomRegistry::with_config. - Room
Handle - A cloneable handle to a live room: the broadcast sender for fan-out, the
command channel to the actor, and the shared member counter. Obtained from
RoomRegistry::get_or_create. - Room
Membership - RAII guard for one member’s presence in a room. Created by
RoomHandle::join. OnDrop(or explicitRoomMembership::leave) it decrements the member count, evicts this member’s presence, and broadcasts apresence_leavedelta — so an abrupt disconnect is still reaped. - Room
Registry - Process-shared registry of live rooms. Cheap to clone (it is an
Arcbag) so it can live inServerStateand be reached from handlers andappend_message.
Enums§
- Frame
- One fan-out frame.
Event/Presencecarry JSON;DocSynccarries opaque bytes so binary CRDT updates pass through without interpretation. Clone is cheap-ish (Value/Vec share via the broadcast clone on each receiver). - Realtime
Channel - The logical channel a
Frametravels on.DocSyncis reserved for Phase 3 CRDT sync and is relayed opaquely for now.