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

PostgreSQL ​

Vix Realtime provides PostgreSQL-backed implementations for authoritative events and room snapshots.

text
PostgresEventStore
    durable event history

PostgresSnapshotStore
    durable recovery checkpoints

Unlike the default memory stores, PostgreSQL data survives process restarts.

Enable PostgreSQL support ​

PostgreSQL support is disabled by default.

Enable it when building the Realtime module:

bash
cmake -S . -B build \
  -DVIX_REALTIME_WITH_POSTGRES=ON

Then build normally:

bash
cmake --build build

The build requires PostgreSQL libpq.

The option controls whether the PostgreSQL implementations are compiled into Realtime.

Check PostgreSQL support ​

At runtime, support can be checked with:

cpp
if (vix::realtime::PostgresEventStore::
        compiled_with_postgres())
{
    // PostgreSQL support is available.
}

The snapshot store provides the same check:

cpp
vix::realtime::PostgresSnapshotStore::
    compiled_with_postgres();

Constructing either PostgreSQL store when Realtime was built without PostgreSQL support throws ErrorCode::MissingDependency.

Connection string ​

Both stores use a standard PostgreSQL libpq connection string.

For example:

text
host=localhost dbname=myapp user=postgres password=secret

A simple event store can be created with:

cpp
auto events =
    std::make_shared<
        vix::realtime::PostgresEventStore>(
            "host=localhost dbname=myapp user=postgres");

A snapshot store is created the same way:

cpp
auto snapshots =
    std::make_shared<
        vix::realtime::PostgresSnapshotStore>(
            "host=localhost dbname=myapp user=postgres");

The connection string cannot be empty.

Event store ​

PostgresEventStore implements the normal EventStore interface.

text
append()
append_batch()
load_after()
latest_event_id()
count()
clear_room()

Code using EventStore does not need different replay or room logic when moving from memory to PostgreSQL.

Event store options ​

For more control, use PostgresEventStoreOptions:

cpp
vix::realtime::PostgresEventStoreOptions options;

options.connectionString =
    "host=localhost dbname=myapp user=postgres";

auto events =
    std::make_shared<
        vix::realtime::PostgresEventStore>(
            options);

The defaults are:

OptionDefault
schema"public"
table"vix_realtime_events"
createSchemaIfMissingfalse
createTableIfMissingtrue
reconnecttrue

Event table creation ​

By default, Realtime creates the event table when it does not exist:

cpp
options.createTableIfMissing = true;

The default table name is:

text
vix_realtime_events

The configured PostgreSQL schema itself is not created by default.

To allow Realtime to create it:

cpp
options.schema = "realtime";
options.createSchemaIfMissing = true;

Custom event table ​

A custom table can be configured:

cpp
options.table = "room_events";

Schema and table names must be valid PostgreSQL-style identifiers accepted by Realtime.

For example:

text
realtime
room_events
events_2026

are valid.

Names containing characters such as - or . are rejected by configuration validation.

Persistent event ordering ​

PostgresEventStore preserves event ordering independently for each room.

text
room-1

EventId 1
EventId 2
EventId 3

The database table enforces uniqueness for:

text
(room_id, event_id)

and:

text
(room_id, room_version)

This prevents duplicate event positions or room versions from being stored for the same room.

Atomic event batches ​

When one command produces several events, Realtime persists them through:

cpp
append_batch();

The PostgreSQL implementation performs the batch inside a transaction.

text
BEGIN
  event 1
  event 2
  event 3
COMMIT

If the operation fails, the transaction is rolled back.

The complete event batch is therefore committed together.

Multiple runtime processes ​

PostgresEventStore uses a PostgreSQL transaction advisory lock for each room while appending events.

Conceptually:

text
Process A ──┐
            |
            v
        Room stream
            ^
            |
Process B ──┘

Only one append transaction can assign the next positions for that room at a time.

This preserves contiguous event IDs and room versions when multiple runtime processes use the same event table.

The database remains the coordination point for persisted event positions.

Snapshot store ​

PostgresSnapshotStore implements the normal SnapshotStore interface.

It supports:

text
save()
load_latest()
load_at_or_before()
load_recent()
count()
prune()
clear_room()

Create one with:

cpp
auto snapshots =
    std::make_shared<
        vix::realtime::PostgresSnapshotStore>(
            "host=localhost dbname=myapp user=postgres");

Snapshot store options ​

Use PostgresSnapshotStoreOptions for custom configuration:

cpp
vix::realtime::PostgresSnapshotStoreOptions options;

options.connectionString =
    "host=localhost dbname=myapp user=postgres";

auto snapshots =
    std::make_shared<
        vix::realtime::PostgresSnapshotStore>(
            options);

The defaults are:

OptionDefault
schema"public"
table"vix_realtime_snapshots"
createSchemaIfMissingfalse
createTableIfMissingtrue
reconnecttrue

The default snapshot table is:

text
vix_realtime_snapshots

Snapshot identity ​

Snapshots are uniquely identified by:

text
room ID
+
room version

For example:

text
room-1
version 42

can have only one stored snapshot position.

Saving the same room version again is allowed only when the existing and replacement snapshots reference the same last_event_id.

A conflicting position is rejected.

Use PostgreSQL with Realtime ​

Create both stores:

cpp
auto events =
    std::make_shared<
        vix::realtime::PostgresEventStore>(
            connectionString);

auto snapshots =
    std::make_shared<
        vix::realtime::PostgresSnapshotStore>(
            connectionString);

Then provide them to RoomManager:

cpp
vix::realtime::Config config;

auto manager =
    std::make_shared<vix::realtime::RoomManager>(
        vix::realtime::NodeId{"node-1"},
        config,
        events,
        snapshots,
        std::make_shared<
            vix::realtime::LocalPresenceStore>(),
        std::make_shared<
            vix::realtime::RoomDirectory>());

vix::realtime::Server server{manager};

Rooms managed by this server now use PostgreSQL for events and snapshots.

Application room handlers and states do not need to change.

Events survive restart ​

With PostgreSQL event storage:

text
process A
   |
   v
persist events
   |
   v
PostgreSQL

process stops

process B
   |
   v
open room
   |
   v
load events

When restoreRoomsOnOpen is enabled, the new runtime can reconstruct the room from its persisted history.

Snapshots survive restart ​

Snapshots are durable in the same way:

text
PostgreSQL snapshot
       |
process restarts
       |
       v
restore snapshot
       |
       v
replay later events
       |
       v
current RoomState

See Replay and Recovery for the complete restoration process.

Connection health ​

Both PostgreSQL stores provide:

cpp
bool healthy =
    events->ping();

and:

cpp
bool healthy =
    snapshots->ping();

ping() returns true when PostgreSQL responds successfully.

It returns false when the connection cannot be used.

Reconnection ​

Automatic connection reset is enabled by default:

cpp
options.reconnect = true;

If the owned PostgreSQL connection enters a failed state, the store attempts to reset it before the next operation.

Disable this behavior with:

cpp
options.reconnect = false;

With reconnection disabled, an unavailable connection causes the operation to fail.

One connection per store ​

Each PostgreSQL store instance owns one libpq connection.

Operations on that store instance are serialized.

text
PostgresEventStore
       |
       v
one libpq connection

The same applies to PostgresSnapshotStore.

Applications that require more database concurrency can use multiple store instances or place a connection-pool adapter behind the persistence interface.

Failure behavior ​

Database failures are reported through Realtime errors.

Event-store failures use:

text
EventStoreFailure

Snapshot-store failures use:

text
SnapshotStoreFailure

For authoritative commands, an event-store failure prevents the room from applying the new events to its state.

text
database append fails
        |
        v
event not committed
        |
        v
state not advanced

This preserves the rule that authoritative events are persisted before state application.

Event store vs snapshot store ​

The two PostgreSQL stores have different responsibilities:

text
PostgresEventStore
    authoritative history
    required for durable event recovery

PostgresSnapshotStore
    recovery checkpoints
    optional optimization

A room can use PostgreSQL events without PostgreSQL snapshots.

When both are configured, recovery can restore a recent snapshot and replay only the events that followed it.

Continue with Protocol for the messages exchanged between Realtime and transport integrations.

Released under the MIT License.