Build on the supported package contract
Use @absolutejs/sync-bus-pg through its supported public entry points.
@absolutejs/sync-bus-pgv0.2.3betaData & SyncTyped Postgres LISTEN/NOTIFY channel and @absolutejs/sync cluster bus — horizontal scale without standing up Redis
bun add @absolutejs/sync-bus-pgPostgres LISTEN/NOTIFY cluster bus for @absolutejs/sync. Run sync horizontally across several Bun processes without standing up Redis or Kafka — your existing Postgres carries the cross-instance change feed.
@absolutejs/sync ships a ClusterBus seam: an in-memory bus for single-process dev + tests, and a contract you implement against your bus of choice for production. Until now that meant writing 50 lines of LISTEN/NOTIFY plumbing yourself. This package is the first-party implementation, with the 8000-byte NOTIFY payload limit handled cleanly.
Every instance of your Elysia app does the same. A mutation committed on instance A now fans out to subscribers on B/C/... via Postgres pg_notify.
spill strategies:
'overflow' (default) — inline JSON when small, table-backed when oversized. Best for typical workloads.
'always' — every message goes through the sync_cluster_spill table (durable, slightly slower; useful when you want every cross-instance change to survive a NOTIFY drop).
'never' — throws if a message exceeds the inline budget. Useful in tests to assert payload-size discipline.
postgres.js automatically recreates its dedicated listener connection and reissues LISTEN after a disconnect. A query-pool health check cannot prove that recovery has completed, so this adapter also sends a private probe through the full pg_notify → dedicated listener path every 15 seconds.
Probe envelopes never reach application callbacks and do not inflate message publish/receive counters. Monitoring starts with the first subscription and stops with the last unsubscribe. Set listenerHealth: false only when another owner calls probeListener() on its own cadence.
Oversized messages spill to sync_cluster_spill. Rows aren't auto-deleted on consume (every listener on the channel needs to read them, including the publisher's own listener which fetches but doesn't double-apply via the engine's origin filter). Sweep periodically:
For workloads where messages stay small (the common case), the spill table never gets touched and vacuum() always returns 0.
Per-instance version cursors. A client that reconnects to a _different_ instance falls back to a fresh snapshot (cold-hydration cost, not catch-up diff). Use sticky sessions if cross-instance reconnect-with-since matters.
Best-effort delivery. Inline NOTIFY can be lost if a listener connection drops mid-stream — every instance also has its own change log for resume, so a missed cross-instance fan-out is recovered on the next subscribe. For at-least-once cross-instance, run with spill: 'always'.
Outcomes
Use @absolutejs/sync-bus-pg through its supported public entry points.
Hardening checklist
Follow in order
Working example for Use.
import postgres from 'postgres';
import { createSyncEngine } from '@absolutejs/sync/engine';
import { createPostgresClusterBus } from '@absolutejs/sync-bus-pg';
const sql = postgres(process.env.DATABASE_URL!);
const engine = createSyncEngine();
// ...registerReader/Writer/Reactive/Mutation as usual...
const bus = createPostgresClusterBus({ sql });
await engine.connectCluster(bus);Working example for Options.
createPostgresClusterBus({
sql, // your postgres client
channel: 'absolutejs_sync_cluster', // override to scope multiple engines on the same PG
spill: 'overflow', // 'overflow' (default) | 'always' | 'never'
listenerHealth: {
// optional; these are the defaults
probeIntervalMs: 15_000,
probeTimeoutMs: 5_000
},
onError: (e) => log.warn(e) // listener-side errors
});Supported entry points declared by this package manifest.
Package entry point declared in package.json.
Scripts declared by this package manifest.
Search the declarations exported by the current package type files. Expand a symbol to inspect its source-backed signature.
type PostgresClusterBusOptions = {
/**
* The `postgres` (https://github.com/porsager/postgres) client. We need
* both a regular SQL connection (for publish + spill fetch) and the
* ability to listen on a channel; `postgres` exposes both via the same
* `Sql` instance.
*/
sql: Sql;
/**
* Channel name passed to `LISTEN` / `pg_notify`. Defaults to
* `'absolutejs_sync_cluster'`. Two engines on the same Postgres can scope
* themselves to different channels by overriding this.
*/
channel?: string;
/**
* Spill strategy. `'overflow'` (default): inline JSON when small, table-
* backed when oversized. `'always'`: every message goes through the spill
* table (durable, slower). `'never'`: throws if a message exceeds the
* inline budget — useful in tests to assert payload-size discipline.
*/
spill?: 'overflow' | 'always' | 'never';
/**
* Called when the listener encounters an error (parse failure, missing
* spill row, etc). Defaults to `console.warn`.
*/
onError?: (error: unknown) => void;
/**
* End-to-end listener monitoring. The bus periodically publishes a private
@absolutejs/sync-bus-pg