Postgres
For the team whose one durable store is SQL. Four providers, one pool — and the only shipped backend that covers reminders as well.
Installation
pnpm add @sigx/actors-pg pgpg (node-postgres) ≥ 8 is a peer dependency; Postgres ≥ 13.
The four providers
pgStorage() — etag-CAS ActorStorage: one row per actor, no
TTL. A conflict throws the branded ActorStorageConflict the runtime turns into
fault-and-reload.
pgMembership() — TTL-heartbeat membership with expiry judged on the database clock,
so host clock skew cannot fake a death or a survival. LISTEN/NOTIFY push with a poll fallback,
and self-suspect fencing when a host cannot prove its own membership.
pgReminders() — durable reminders on an indexed table. A tick is one
FOR UPDATE SKIP LOCKED claim statement for exactly the due rows, and the advance or delete
commits before delivery — at-most-once, no catch-up bursts, database clock throughout.
Every host may tick the same table, because row locks replace shard ownership. A dispatch that
fails is re-armed one reminderTickMs out on the database clock and counted in
HostStats.remindersUndelivered — the shared reminder contract.
pgDirectory() — the single-activation claim directory: create-if-absent claim,
compare-and-delete release and evict, plus an evictHost sweep for departed hosts.
pgCluster() bundles membership and directory.
Why you might pick it over Redis
- You already run Postgres and would rather not add Redis.
- Reminder tables get large. The default
shardedReminders()rewrites a sixteenth of the table on everyset;pgReminders()is an index, so an arm is O(log N) and a tick O(due). (Redis has the same answer inredisReminders()— see choosing a provider.) - Clock skew worries you. Expiry on the database clock removes a whole class of split-brain question.
DDL is explicit
The providers never issue DDL on their own — it happens only when you call for it, so a production role that never runs the bootstrap needs only DML grants.
await ensurePgSchema(pool); // safe from every replica, concurrently
// or: run pgSchemaSql() through the migration tool that owns your schema
Next steps
- Installation — schema, wiring and options.
- API reference — exports and table layout.
- Clustering — where the providers plug in.
