Session Resume
vix::realtime::SessionResume reconnects a detached logical session through a new transport connection.
The same session keeps its identity, room memberships, and recovery positions.
Session
|
connection lost
|
v
Detached
|
new connection
|
v
ConnectedSession resumption is enabled by default.
Create the resume service
Create SessionResume from the room manager:
vix::realtime::SessionResume resume{
server.manager()};The service uses the manager's session registry, event store, snapshot store, presence store, and Realtime configuration.
Issue a resume token
A session needs a resume token before it can be resumed.
auto token = resume.issue(
session->id());The token is stored on the logical session.
If the session already had a token, issuing a new one immediately replaces it.
Resume tokens should be treated as credentials and sent only to the client that owns the session.
Disconnect the session
When the transport connection disappears, detach it through the runtime:
server.disconnect(
session->id(),
connectionId);The session becomes Detached, but remains registered.
Its room memberships are preserved.
Connected
|
v
Detached
rooms remain joined
session remains registeredThis is what makes later resumption possible.
Resume the session
When the client reconnects with its token:
auto result = resume.resume(
session->id(),
token,
newConnection);On success:
result.session;
result.resumeToken;
result.tokenRotated;The logical session is now attached to newConnection.
Resume requirements
A resume operation succeeds only when:
- session resumption is enabled
- the session still exists
- the session is not permanently closed
- the session is detached
- the supplied token matches
- the resume window has not expired
- the replacement connection exists
- the replacement connection has an identifier
- the replacement connection is open
- the session does not exceed the configured resume room limit
- required room recovery succeeds
If one of these conditions fails, resume() throws a Realtime error.
Resume window
The maximum detached duration is configured with:
config.sessionResumeWindow =
std::chrono::seconds{120};The default is 120 seconds.
A session detached longer than this window cannot be resumed.
detach
|
|------ resume window ------|
|
v
expiredCheck the effective window with:
auto window =
resume.resume_window();Check whether a session can resume
You can check eligibility before attempting a resume:
if (resume.can_resume(
session->id(),
token))
{
// Session is currently resumable.
}can_resume() verifies the token and the current session lifecycle state.
It does not attach a connection.
Check only the token
Use matches() when you only need to verify token ownership:
bool valid = resume.matches(
session->id(),
token);This check does not require the session to be detached or inside the resume window.
The distinction is:
matches()
token belongs to session
can_resume()
token matches
+
session can currently resumeMissing room events
A detached session may miss events while it has no connection.
Each session stores its last known event position for joined rooms.
For example:
room events
1 2 3 4 5
^
|
session cursorIf the session last received event 3, resumption attempts to recover events 4 and 5.
The recovery happens before the new connection becomes the active session connection.
Multiple rooms
Recovery is performed independently for every room joined by the session.
Session
|
+---- Room A
| recover missing events
|
+---- Room B
recover missing eventsThe maximum number of rooms that can participate in one resume operation is controlled by:
config.maxResumeRoomsThe default is:
32A session that belongs to more rooms than this limit cannot resume.
Replay limits
Missing events are recovered using the configured replay limits:
config.maxReplayEvents;
config.maxReplayBytes;
config.replayTimeout;These limits prevent one resume operation from performing unbounded recovery work.
For example, if too many events are missing, direct replay may not be possible.
Snapshot fallback
When direct event replay exceeds the event limit, Realtime can use a suitable stored snapshot when a snapshot store is available.
Conceptually:
session cursor
|
| too much history
v
latest usable snapshot
|
v
events after snapshot
|
v
current positionThe replacement connection receives the snapshot first, followed by events that occurred after it.
If recovery still exceeds the configured limits, the resume operation fails.
See Replay and Recovery for the detailed replay model.
Recovery order
A successful resume follows this model:
validate session and token
|
v
recover joined rooms
|
v
send missing snapshots/events
|
v
attach new connection
|
v
restore presence
|
v
update room cursors
|
v
resume completeThe session cursors are updated only after room recovery succeeds.
This prevents a failed recovery from falsely recording events as recovered.
Recovery failure
If recovery fails before the replacement connection is attached, the logical session remains detached.
Its existing room cursors remain unchanged, and its previous resume token remains valid.
For example:
Room A recovery succeeds
Room B recovery fails
|
v
resume fails
session remains detached
cursors remain unchanged
old token remains validThe candidate connection may already have received recovery messages from an earlier room before a later room fails.
Those bytes cannot be taken back from the transport. The rollback applies to the server-side session state.
Token rotation
By default, a successful resume rotates the token.
auto result = resume.resume(
session->id(),
token,
newConnection);
auto nextToken =
result.resumeToken;After successful rotation:
old token
invalid
new token
used for next resumeCheck whether rotation occurred with:
result.tokenRotated;The new token should be returned to the client and stored for the next reconnection.
Resume without token rotation
Token rotation can be disabled for a specific call:
auto result = resume.resume(
session->id(),
token,
newConnection,
vix::realtime::SystemClock::now(),
false);In that case, the current token remains valid.
For normal use, keep the default rotation behavior.
Rotate a token manually
A token can also be rotated explicitly:
auto newToken =
resume.rotate(session->id());The previous token becomes invalid immediately.
rotate() has the same token replacement behavior as issuing a new token.
Revoke a token
Remove the current resume credential with:
resume.revoke(
session->id());Revoking the token does not close the session or its current connection.
It only prevents that token from being used for future resumption.
Presence after resume
When presence is enabled, connection detachment marks the session presence as Detached.
A successful resume attaches the replacement connection through RoomManager, which restores joined-room presence to Present.
Present
|
disconnect
v
Detached
|
resume
v
PresentPresence is separate from authoritative room state.
Common resume failures
An unknown session produces:
SessionNotFoundAn incorrect or invalid token produces:
InvalidResumeTokenTrying to resume an already connected session produces:
SessionAlreadyConnectedA session that has never entered the detached state produces:
SessionNotDetachedAn expired or permanently closed session produces:
SessionExpiredAn invalid replacement connection produces:
ConnectionNotAttachedRecovery limits can produce:
ReplayLimitExceededIncomplete recovery can produce:
ReplayUnavailableBasic workflow
The normal workflow is:
vix::realtime::SessionResume resume{
server.manager()};
auto token =
resume.issue(session->id());
// Connection is later detached.
auto result =
resume.resume(
session->id(),
token,
newConnection);
token = result.resumeToken;The important model is:
issue token
|
v
session disconnects
|
v
session stays detached
|
v
client reconnects
|
v
recover missed room data
|
v
attach new connection
|
v
rotate tokenSession resumption preserves the logical session while allowing its transport connection to change.
Continue with Presence for how connected, detached, and departed participants are represented.