Room Ownership
Room ownership identifies which Realtime node is responsible for operating a logical room.
Room
|
v
RoomOwner
|
v
NodeIdRoomManager uses a RoomDirectory to coordinate this ownership.
Automatic ownership
Normal applications do not need to acquire ownership manually.
When a manager opens a room:
auto room = manager.open_room(
vix::realtime::RoomId{"room-1"},
"counter");the manager acquires ownership for its local node before creating the room.
Conceptually:
open_room()
|
v
acquire ownership
|
v
create room
|
v
open roomIf room creation or opening fails, the manager releases the ownership claim.
Room owner
A RoomOwner describes one ownership claim.
It contains:
room ID
node ID
generation
status
acquisition time
optional lease expiration
metadataFor example:
auto owner =
directory.resolve(
vix::realtime::RoomId{"room-1"});
if (owner)
{
const auto &node =
owner->node_id();
}Ownership status
An ownership claim has three possible states:
Active
Releasing
ReleasedActive
An active owner may operate the room.
if (owner->status() ==
vix::realtime::RoomOwnerStatus::Active)
{
// Ownership is active.
}Releasing
Releasing means the owner is in the process of giving up the room.
A releasing claim is no longer returned by resolve(), but it continues to block another acquisition until it is fully released or its lease expires.
Released
Released means the ownership claim has ended.
Released ownership is considered expired and cannot operate the room.
Resolve an owner
Find the current active owner with:
auto owner =
directory.resolve(roomId);If there is no active owner:
owner.has_value(); // falseresolve() does not return:
expired claims
releasing claims
released claimsInspect a claim
Use inspect() when you need to see the currently stored claim even if it is not active:
auto owner =
directory.inspect(roomId);Unlike resolve(), inspect() may return a releasing or expired claim.
The distinction is:
resolve()
active ownership only
inspect()
currently stored ownership descriptorOwnership generation
Every ownership claim has a generation:
auto generation =
owner->generation();The generation begins at:
1and increases whenever a new ownership claim replaces an older one.
For example:
node-a owns room
generation 1
ownership released
node-b acquires room
generation 2A later owner cannot reuse an older generation.
Why generations matter
A node identifier alone is not enough to identify one ownership claim.
Consider:
node-a
generation 1
|
v
ownership ends
node-a
generation 2These are two different ownership periods.
Realtime can therefore validate both:
NodeId
+
generationwhen checking whether an operation belongs to the current ownership claim.
Check ownership
Check whether a node currently owns a room:
bool owns =
directory.owns(
roomId,
nodeId);To verify the complete ownership identity:
bool matches =
directory.matches(
roomId,
nodeId,
generation);matches() requires an active claim with the same node and generation.
Acquire ownership
Ownership can also be acquired directly:
vix::realtime::RoomDirectory directory;
auto owner =
directory.acquire(
vix::realtime::RoomId{"room-1"},
vix::realtime::NodeId{"node-1"});Without a lease duration, the ownership does not expire automatically.
Only one unexpired ownership claim can exist for a room.
Attempting to acquire the same room while another valid claim exists fails.
Release ownership
Release a current ownership claim with:
auto released =
directory.release(
roomId,
nodeId,
generation);The current claim is removed from the directory.
Its generation history remains recorded.
For example:
generation 3
|
release
|
v
no active owner
latest generation remains 3A later acquisition receives generation 4.
Latest generation
Read the latest generation observed for a room with:
auto generation =
directory.latest_generation(
roomId);If the directory has never observed the room:
0is returned.
Generation history remains available after normal release or expired-claim pruning.
Releasing ownership
A claim can first enter the releasing state:
auto owner =
directory.begin_release(
roomId,
nodeId,
generation);The lifecycle becomes:
Active
|
v
Releasing
|
v
ReleasedWhile releasing, resolve() no longer reports the claim as an active owner.
Another node still cannot acquire the room until the claim is released or expires.
Transfer ownership
Ownership can be transferred directly to another node:
auto next =
directory.transfer(
roomId,
currentNode,
currentGeneration,
vix::realtime::NodeId{"node-2"});Transfer creates a new active claim with the next generation.
For example:
node-1
generation 4
|
v
transfer
|
v
node-2
generation 5The transfer replaces the current owner atomically inside the local directory.
Ownership leases
Ownership can optionally expire after a configured duration.
For example:
auto owner =
directory.acquire(
roomId,
nodeId,
std::chrono::seconds{30});This creates a lease:
acquired
|
| 30 seconds
v
expiresThe lease duration must be positive.
Check for a lease
Use:
if (owner.has_lease())
{
auto expiration =
owner.expires_at();
}A claim without an expiration timestamp is permanent until explicitly released or transferred.
Lease expiration
Check whether a claim has expired:
bool expired =
owner.expired(
vix::realtime::SystemClock::now());An expired claim is no longer considered active.
Active
|
lease expires
|
v
not activeresolve() therefore stops returning it.
Renew a lease
An active lease can be renewed:
auto renewed =
directory.renew(
roomId,
nodeId,
generation,
std::chrono::seconds{30});The new expiration begins from the renewal time.
old expiration
|
renew
|
v
new expirationAn expired or inactive claim cannot be renewed.
Make ownership permanent
A leased claim can be converted into non-expiring ownership:
auto owner =
directory.make_permanent(
roomId,
nodeId,
generation);Its expiration timestamp is removed.
The claim remains active until explicitly released or transferred.
Remove expired claims
Expired claims can be removed from the directory:
auto removed =
directory.prune_expired();Generation history is preserved.
expired generation 7
|
v
prune_expired()
|
v
claim removed
latest generation = 7The next acquisition therefore receives generation 8.
Ownership metadata
An ownership claim can contain non-authoritative metadata.
vix::realtime::JsonObject metadata;
metadata.set_string(
"region",
"east");
auto owner =
directory.acquire(
roomId,
nodeId,
std::nullopt,
vix::realtime::SystemClock::now(),
std::move(metadata));Read it with:
owner.metadata();Ownership metadata must not be treated as authoritative room state.
Application state that needs persistence and replay belongs in RoomState and room events.
List active owners
Get all active ownership claims with:
auto owners =
directory.active_owners();The results are sorted by room identifier.
To list active rooms owned by one node:
auto owners =
directory.owned_by(nodeId);RoomManager ownership
RoomManager uses its own NodeId when opening rooms.
const auto &nodeId =
manager.node_id();The normal relationship is:
RoomManager
|
| NodeId
v
RoomDirectory
|
v
RoomOwner
|
v
RoomWhen the room closes successfully, the manager releases its ownership claim.
Applications using normal Server or RoomManager room operations therefore do not need to manage ownership manually.
Process-local directory
The built-in RoomDirectory is process-local.
Process A
|
v
RoomDirectory A
Process B
|
v
RoomDirectory BThese directories do not automatically share ownership information.
The ownership model provides concepts needed for coordination, including:
NodeId
generation
leases
transfer
renewal
releasebut the built-in directory is not a distributed cluster coordinator.
Applications that need ownership shared across multiple machines require an external coordination implementation that preserves the same ownership rules.
Main model
The important ownership relationship is:
RoomId
|
v
RoomOwner
|
+---- NodeId
+---- Generation
+---- Status
+---- Optional leaseFor normal single-process Realtime use, RoomManager handles this automatically.
The generation and lease model provides the ownership semantics required for safer coordination when ownership changes over time.
Continue with Distributed Presence for the interface used to extend presence coordination beyond one process.