Actors/Packages/Postgres
@sigx/actors-pg · Preview

Postgres#

For the team whose one durable store is SQL. Four providers, one pool — and the only shipped backend that covers reminders as well.

v0.1.0 MIT

Installation#

Terminal
pnpm add @sigx/actors-pg pg

pg (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.

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 SKIP LOCKED design is the scan answer; Redis rides storage with 16 hash shards, which is fine until it is not.
  • 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, so a production role needs only DML grants.

TypeScript
await ensurePgSchema(pool);   // dev and tests
// production: run pgSchemaSql() through your migration tool

Next steps#