Pingerchips LogoPingerchips
SpacesReference

Architecture

Channel topology

All Spaces traffic flows over one Phoenix channel per space:

app:{appKey}:room:ephemeral-{spaceId}
Loading diagram…

Two distinct paths

Loading diagram…

Cursors and locations never touch the SpaceWorker. At 100 members × 30fps = 3,000 events/sec, routing them through a single GenServer mailbox would be a guaranteed bottleneck.


Cursor throttle

Loading diagram…

The throttle is enforced in the SDK. Intermediate positions within the 33ms window are dropped — only the latest is sent.


Presence — Phoenix Tracker

Loading diagram…

No explicit cleanup needed — Tracker fires presence:leave and terminate/2 releases locks on any disconnect, including crashes and network drops.


Lock acquire / release

Loading diagram…

SpaceWorker is one GenServer per {app_id, space_id}. All lock operations are serialised through its mailbox. Lock state is in-memory only — no RocksDB, no WAL. A worker crash or node restart clears all locks (equivalent to all members disconnecting simultaneously).


Supervision tree

Loading diagram…

SpaceWorker is :transient — not restarted on crash. Next lock acquire starts a fresh worker with an empty lock map. Members stay connected; only lock state is lost.


Scale

DimensionBehaviour
Cursor fanoutO(n) broadcast_from! per space
Lock throughputLimited by SpaceWorker mailbox — but lock events are rare vs cursors
Idle spacesSpaceWorker exits after 10min; no persistent state
Max concurrent spacesUnbounded — stateless relay + lazy GenServer

On this page