Vix.cpp v2.8.5 is here Read the blog
Skip to content

Distributed Presence

vix::realtime::DistributedPresence defines the contract for sharing presence information across multiple Realtime runtime nodes.

It extends the normal PresenceStore interface with node heartbeats and backend health information.

text
Node A ──┐
         |
Node B ──┼── shared presence backend
         |
Node C ──┘

Presence remains non-authoritative application information. Authoritative room state continues to belong to persisted events and RoomState.

Current implementation status

Realtime currently provides the DistributedPresence interface, but does not include a built-in Redis, PostgreSQL, or other concrete distributed presence backend.

The default runtime uses:

cpp
vix::realtime::LocalPresenceStore

which is process-local.

DistributedPresence is the public contract for applications that need to provide a shared presence backend.

PresenceStore compatibility

DistributedPresence inherits from:

cpp
vix::realtime::PresenceStore

A distributed implementation must therefore support the normal presence operations:

text
upsert
find
touch
mark present
mark detached
mark left
list room
list session
erase
prune stale
clear room
clear session

It additionally manages runtime-node presence.

Local node

Every distributed backend identifies its local runtime node:

cpp
const auto &nodeId =
    presence->local_node_id();

The node identifier distinguishes one Realtime runtime from another.

text
node-1
node-2
node-3

Node heartbeat

A distributed backend publishes the local node heartbeat through:

cpp
presence->heartbeat();

The heartbeat indicates that the runtime node is still active.

Conceptually:

text
node-1
   |
heartbeat
   |
   v
shared presence backend

A heartbeat can also include non-authoritative node metadata:

cpp
vix::realtime::JsonObject metadata;
metadata.set_string("region", "east");

presence->heartbeat(
    vix::realtime::SystemClock::now(),
    std::move(metadata));

The backend implementation decides how that heartbeat is persisted and shared.

Find a node

Find one known runtime node with:

cpp
auto node =
    presence->find_node(
        vix::realtime::NodeId{"node-1"});

If the node is unknown:

cpp
node.has_value(); // false

A known node contains:

text
NodeId
last heartbeat time
local flag
metadata

List nodes

List all known nodes with:

cpp
auto nodes =
    presence->nodes();

Implementations should return them in deterministic node identifier order.

Active nodes

Find nodes whose heartbeat is still recent:

cpp
auto nodes =
    presence->active_nodes(
        vix::realtime::SystemClock::now(),
        std::chrono::seconds{30});

A node is active when its last heartbeat is newer than the specified timeout.

For example:

text
node-1 last seen 5s ago
node-2 last seen 12s ago
node-3 last seen 40s ago

timeout = 30s

active:
node-1
node-2

Check one node

Check whether one node is active with:

cpp
bool active =
    presence->node_active(
        nodeId,
        vix::realtime::SystemClock::now(),
        std::chrono::seconds{30});

The node must exist and its heartbeat must still be recent.

Stale nodes

DistributedPresenceNode can check whether its heartbeat is stale:

cpp
bool stale =
    node.stale(
        vix::realtime::SystemClock::now(),
        std::chrono::seconds{30});

A timeout less than or equal to zero treats the node as stale.

Prune stale nodes

A distributed implementation can remove expired node records:

cpp
auto removed =
    presence->prune_stale_nodes(
        vix::realtime::SystemClock::now(),
        std::chrono::seconds{30});

The return value is the number of removed node records.

The backend may also remove presence records belonging to those nodes according to its own policy.

Clear one node

Remove all presence owned by one node with:

cpp
auto removed =
    presence->clear_node(nodeId);

This operation is useful when a runtime node is intentionally removed from the shared presence system.

It returns the number of removed presence records.

Backend status

A distributed presence backend reports one of three states:

text
Healthy
Degraded
Unavailable

Read it with:

cpp
auto status =
    presence->distributed_status();

Healthy

text
healthy

means the shared backend is reachable and operating normally.

Degraded

text
degraded

means the backend remains usable but is experiencing partial failures.

Unavailable

text
unavailable

means the shared backend cannot currently provide its normal service.

Ping the backend

Check whether the shared backend responds with:

cpp
bool available =
    presence->ping();

ping() is intended as a lightweight backend reachability check.

It does not determine whether a particular session or node is present.

Distributed presence and room presence

A distributed backend still stores normal logical presence records.

For example:

text
room-1
   |
   +---- session-1 on node-1
   +---- session-2 on node-2

Because DistributedPresence implements PresenceStore, normal Realtime operations can continue using presence concepts such as:

text
Present
Detached
Left

The difference is where those records are coordinated.

text
LocalPresenceStore

one process only

compared with:

text
DistributedPresence

shared coordination backend
multiple runtime nodes

Distributed presence is not room ownership

Distributed presence and room ownership solve different problems.

text
DistributedPresence
    where sessions and runtime nodes are present

RoomDirectory
    which node owns a room

Knowing that node-2 is active does not by itself mean that node-2 owns a particular room.

See Room Ownership for room ownership semantics.

Distributed presence is not authoritative state

Presence should not contain application facts that must survive replay.

For example:

text
player score
document contents
match result

belong in authoritative room events and RoomState.

Distributed presence is intended for information such as:

text
session participation
connection state
runtime node
last activity
node heartbeat

The separation remains:

text
RoomState
    authoritative application state

DistributedPresence
    shared runtime participation

Implementing a backend

A custom backend derives from:

cpp
class MyPresence
    : public vix::realtime::DistributedPresence
{
    // Implement PresenceStore
    // and DistributedPresence operations.
};

The implementation must provide both:

text
normal presence storage
+
node coordination

A backend could use a shared system such as PostgreSQL, Redis, or another coordination service, but Realtime does not currently provide those concrete distributed implementations.

Main model

The distributed presence contract is:

text
Realtime Node
     |
     +---- heartbeat
     |
     +---- session presence
     |
     v
DistributedPresence
     |
     v
shared backend

The interface defines how multiple runtime nodes can share presence and node-health information while keeping presence separate from authoritative room state.

Continue with Metrics for runtime counters and duration measurements.

Released under the MIT License.