Skip to content

How Each Mode Works

single

Each tick, the Processor calls pgrelay.should_dispatch_single() (which simply returns NOT pg_is_in_recovery() — PostgreSQL's own function for "am I currently a standby replaying changes?"). On a writable primary, it dispatches normally; on a read-only standby, it idles and logs standby_waiting. On promotion, it logs primary_active and starts dispatching. Failover latency here is entirely down to how fast your HA layer promotes the standby.

multi-node

  • At startup, and on every reload, before its first tick, the Processor runs pgrelay._startup_queue_check() — a catch-all that re-homes any pending row whose owner is not a configured Spock node (in other words, a decommissioned or unrecognised owner) onto this node, so nothing is ever left permanently stuck.
  • On every tick, before dispatching, it runs pgrelay.multi_master_watch() — the conservative adoption watcher (see Deciding If a Node Is Dead). It then dispatches its own owned rows (the owner_node filter, applied inside queue_pending_ids()/_queue_claim(), restricts each node to only the rows where owner_node matches its own node).

leader

Each tick, the Processor calls pgrelay.is_lowest_live_master() and publishes the result to its own connections as the pg_relay.is_leader session setting (logging leader_acquired / leader_yielded whenever this changes). Every node then goes on to dispatch — what leadership actually changes is only the scope the SQL dispatch rule grants it:

  • the leader is offered its own node-restricted rows, plus every unrestricted row, regardless of which node created it;
  • a non-leader is offered only its own node-restricted rows (see Node-Restricted Channels). With no restricted channels registered, its candidate query simply returns nothing — one cheap, indexed query per tick, and no wasted work.

A row offered to a node under stale leadership (for example, if leadership changed hands a moment ago) fails re-validation at the moment it is claimed — _queue_claim() applies the same rule — and is simply skipped.


Continue to Deciding If a Node Is Dead for how adoption and leader election actually decide a peer has failed.