beryl/presence
Presence - Distributed presence tracking backed by a CRDT
Wraps the pure lattice_presence/presence_state CRDT in an OTP actor that:
- Handles track/untrack calls
- Periodically broadcasts state via PubSub for cross-node replication
- Receives remote state from PubSub and merges it internally
- Invokes
on_diffcallback when merges produce non-empty diffs
When running beryl under beryl/supervisor, enable presence with
supervisor.with_presence and obtain the handle from
supervisor.presence instead of calling start directly.
Example
Section titled “Example”let ps = pubsub.start(pubsub.default_config())let config = presence.default_config("node1") |> presence.with_pubsub(ps) |> presence.with_broadcast_interval(1500)let assert Ok(p) = presence.start(config)let ref = presence.track(p, "room:lobby", "user:1", "socket-1", meta)let entries = presence.list(p, "room:lobby")Config
Section titled “Config”Configuration for starting presence.
Build configs with default_config and the with_* functions so beryl can
add future options without exposing record fields as public API.
pub type ConfigAn opaque diff representing presence joins and leaves grouped by topic.
This is passed to Config.on_diff and accepted by
beryl.broadcast_presence_diff.
pub type DiffMessage
Section titled “Message”Messages the presence actor handles
pub type MessagePresence
Section titled “Presence”A running Presence instance.
This handle is intentionally opaque so callers cannot forge actor subjects or depend on the runtime representation.
pub type PresencePresenceEntry
Section titled “PresenceEntry”A presence entry returned from queries and diff accessors.
This type is intentionally transparent so callers can inspect query results
and construct entries for diff.
pub type PresenceEntry { PresenceEntry( session_id: String, key: String, meta: json.Json )}PresenceError
Section titled “PresenceError”Errors from presence operations
pub type PresenceError { PresenceStartFailed(error.StartFailure)}Constructors
Section titled “Constructors”PresenceStartFailed(error.StartFailure)
Section titled “PresenceStartFailed(error.StartFailure)”The presence actor failed to start.
Functions
Section titled “Functions”default_config
Section titled “default_config”Default configuration (no PubSub).
The broadcast interval defaults to 1500 ms so that adding with_pubsub
yields working two-way replication out of the box; without PubSub the
interval is unused. Use with_broadcast_interval(0) to disable periodic
broadcasts and control replication manually.
pub fn default_config(String) -> ConfigBuild a presence diff from topic-grouped joins and leaves.
Most applications receive diffs from Config.on_diff; this helper is for
callers that need to construct a diff to pass to beryl.broadcast_presence_diff.
pub fn diff( joins: List(#(String, List(PresenceEntry))), leaves: List(#(String, List(PresenceEntry)))) -> Diffdiff_joins
Section titled “diff_joins”Get presence joins for a topic in this diff.
pub fn diff_joins( Diff, String) -> List(PresenceEntry)diff_leaves
Section titled “diff_leaves”Get presence leaves for a topic in this diff.
pub fn diff_leaves( Diff, String) -> List(PresenceEntry)diff_topics
Section titled “diff_topics”List topics touched by this diff.
pub fn diff_topics(Diff) -> List(String)get_by_key
Section titled “get_by_key”Get presences for a specific key within a topic
Panics if the presence actor is unavailable or does not reply within 5 seconds.
pub fn get_by_key( Presence, String, String) -> List(#(String, json.Json))List all presences for a topic
Panics if the presence actor is unavailable or does not reply within 5 seconds.
pub fn list( Presence, String) -> List(PresenceEntry)Start the presence actor
pub fn start(Config) -> Result(Presence, PresenceError)Track a presence in a topic.
session_id identifies the session (e.g. socket) that owns this presence
and is the value untrack_all matches on when the session disconnects.
Returns a server-generated tracking ref: an opaque, unique handle for this
specific presence. Pass it to untrack to remove exactly this entry later.
The ref is not the session id — it is minted by the presence actor and is
only meaningful to that actor. The ref is also merged into object metas as
phx_ref for Phoenix client compatibility.
Panics if the presence actor is unavailable or does not reply within 5 seconds.
pub fn track( Presence, String, String, String, json.Json) -> Stringuntrack
Section titled “untrack”Untrack a specific presence using the ref returned by track.
Removing an unknown or already-removed ref is a harmless no-op.
Panics if the presence actor is unavailable or does not reply within 5 seconds.
pub fn untrack( Presence, String) -> Niluntrack_all
Section titled “untrack_all”Untrack all presences for a session (e.g., when a socket disconnects)
Panics if the presence actor is unavailable or does not reply within 5 seconds.
pub fn untrack_all( Presence, String) -> Nilwith_broadcast_interval
Section titled “with_broadcast_interval”Set how often presence state is broadcast for replication.
Use 0 to disable periodic broadcasts.
pub fn with_broadcast_interval( Config, Int) -> Configwith_on_diff
Section titled “with_on_diff”Set the callback invoked when local changes or remote merges produce a diff.
pub fn with_on_diff( Config, fn(Diff) -> Nil) -> Configwith_pubsub
Section titled “with_pubsub”Enable PubSub replication for presence.
pub fn with_pubsub( Config, pubsub.PubSub(SyncPayload)) -> Config