Rooms
A Room is the authoritative runtime for one shared unit of Realtime state.
A room contains application state, processes commands in order, persists accepted events, manages session membership, and can restore its state from persisted history.
Examples of room identifiers include:
chat/general
game/match-42
document/123Open a room
Rooms are normally opened through Server.
First register the room factory:
server.register_factory(factory);
server.start();Then open a room:
auto room = server.open_room(
vix::realtime::RoomId{"room-1"},
"counter");The first argument is the room identifier.
The second argument is the registered room type.
Room identity
Read the room identifier with:
const auto &id = room->id();Read its type with:
const auto &type = room->type();The room identifier distinguishes one room instance from another.
The room type determines which RoomFactory creates its state and handler.
type: counter
room-1
room-2
room-3Each room has independent state and event history.
Room lifecycle
A room follows this lifecycle:
Created
|
v
Opening
|
v
Open
|
v
Closing
|
v
ClosedA room may enter Failed when an unrecoverable runtime or state error occurs.
Check the current status with:
auto status = room->status();For common checks:
if (room->is_open())
{
// The room can process commands.
}if (room->failed())
{
// The room entered a failure state.
}A room accepts normal commands and membership changes only while it is open.
Authoritative state
The current state is available through:
const auto &state = room->state();A serialized copy can be obtained with:
auto state = room->serialize_state();Application code should not modify the room state directly.
State changes normally follow this path:
command
|
v
RoomHandler
|
v
RoomEvent
|
v
EventStore
|
v
RoomState::apply()This keeps the event history and current state consistent.
Execute a command
A command can be executed directly:
vix::realtime::JsonObject payload;
payload.set_i64("amount", 1);
vix::realtime::RoomCommand command{
room->id(),
sessionId,
"counter.increment",
std::move(payload)};
auto result = room->execute(command);The command must target the room being executed.
If the handler accepts the command and produces events, the room persists those events before applying them to its state.
Accepted commands
An accepted command may produce one or more events.
RoomCommand
|
v
CommandResult
|
+---- event 1
+---- event 2The room assigns the authoritative room version and persistent event position during commit.
The returned result contains the persisted events.
Rejected commands
A handler can reject a command:
return vix::realtime::CommandResult::rejected(
vix::realtime::ErrorCode::InvalidCommand,
"invalid value");A normal rejected command does not change the room state or event stream.
It also does not by itself place the room in Failed.
Expected versions
A command can require a specific room version:
command.set_expected_version(
room->version());The command is accepted for processing only if the room is still at that version.
If another state change has already advanced the room, the command is rejected.
This provides optimistic concurrency control.
Room version
Read the current logical state version with:
auto version = room->version();A persisted event that changes the authoritative state advances the room version.
For example:
initial state version 0
event 1 version 1
event 2 version 2
event 3 version 3Event position
Read the latest persisted event identifier with:
auto eventId = room->last_event_id();RoomVersion and EventId are different concepts.
RoomVersion
current logical state version
EventId
position in the persisted event streamApplications should not treat them as interchangeable identifiers.
Queue commands
Commands can also be placed in the room queue:
auto status = room->enqueue(
std::move(command));Process the oldest queued command with:
auto result = room->process_next();If the queue is empty, process_next() returns no result.
The number of waiting commands is available with:
auto count = room->pending_command_count();The queue is bounded by:
config.maxPendingCommandsPerRoomCommand ordering
Commands for one room are processed serially.
This means one room does not apply two authoritative command transitions at the same time.
command A
|
v
event A
|
v
state
command B
|
v
event B
|
v
stateEach command therefore observes the authoritative state produced by earlier completed commands.
Different rooms maintain independent state and execution.
Join a session
A logical session can join an open room:
auto result = room->join(sessionId);For normal application workflows, prefer the server operation:
server.join_room(
sessionId,
room->id());The server coordinates both sides of the membership relationship and presence when it is enabled.
Check membership
Check whether a session belongs to the room:
if (room->has_session(sessionId))
{
// Session belongs to this room.
}Get the number of members:
auto count = room->session_count();Get all session identifiers:
auto sessions = room->sessions();Check whether the room has no members:
if (room->empty())
{
// No sessions are currently members.
}Leave a room
A session can leave with:
auto result = server.leave_room(
sessionId,
room->id());The room handler can receive the leave lifecycle operation before the membership is removed.
If that lifecycle operation is rejected, the existing membership is preserved.
Room capacity
The maximum number of sessions in one room is controlled by:
config.maxSessionsPerRoomWhen the room reaches this limit, another session cannot join until capacity becomes available.
Snapshots
A snapshot can be created explicitly:
auto snapshot = room->snapshot();The snapshot contains a serialized copy of the authoritative state together with its room version and latest event position.
Snapshot storage is optional.
If no SnapshotStore is configured, forcing a snapshot fails.
Automatic snapshots are controlled by the Realtime configuration and are covered in Snapshots.
Room restoration
When room restoration is enabled, open() reconstructs persisted state before the room becomes available.
The normal recovery path is:
snapshot, if available
|
v
later persisted events
|
v
RoomState
|
v
OpenIf no snapshot is available, recovery can replay the persisted event stream from the beginning.
See Replay and Recovery for details.
Close a room
Rooms are normally closed through Server:
auto result = server.close_room(
room->id());During a normal close, the room:
- runs the handler's close lifecycle operation
- commits any accepted lifecycle events
- creates a final snapshot when configured
- removes its session memberships
- closes its command queue
- enters
Closed
If the handler rejects the close operation, the room returns to Open.
Room activity
The latest activity timestamp is available with:
auto lastActivity = room->last_activity_at();This information is used by idle-room cleanup.
An empty room can become eligible for cleanup after:
config.roomIdleTimeoutCleanup is performed explicitly by the runtime. The room does not create its own background cleanup thread.
Metadata
A room can contain non-authoritative metadata:
vix::realtime::JsonObject metadata;
metadata.set_string("region", "eu");
room->set_metadata(std::move(metadata));Read it with:
auto metadata = room->metadata();Room metadata is separate from authoritative RoomState.
Values that define application state and must be reconstructed from persisted history should live in the event-driven room state instead.
Ownership
A room may record the node that currently owns it:
auto owner = room->owner_node_id();Ownership is normally coordinated by RoomManager and RoomDirectory.
Applications should not usually set room ownership directly.
See Room Ownership for the ownership model.
Important behavior
The main room guarantees are:
commands are processed serially
accepted events are persisted before state application
rejected commands do not mutate authoritative state
room state can be reconstructed from persisted events
snapshots are optional
session membership belongs to the logical room
transport delivery happens after authoritative processingA Room is therefore the boundary where application commands become persisted authoritative state changes.
Continue with Room State for defining the state that a room owns.