ergon_job_notifier (ergon v0.5.0)
View SourceTurns ergon.jobs into a reactive queue: one LISTEN connection that wakes the
right workers the instant a runnable job lands, instead of making them wait out
their poll interval.
One instance runs per node, subscribing to the fixed channel
ergon_job_available on the node's shared ergon_listener connection. The
emitting half is the pg_cron tick installed by
priv/migrations/cron: ergon.notify_pending_jobs() runs every second and fires
pg_notify('ergon_job_available', queue) once per queue holding immediately
runnable work. Each notification's payload is the queue name only, never job
data or tenant, since NOTIFY bypasses row-level security, and the notifier
hands it to ergon_worker_registry:wake/1.
Writers never call pg_notify
This is the architectural point, and it is easy to undo by accident. A
transaction with a pending NOTIFY takes the global notification-queue lock at
commit, which serialises every notifying commit. A trigger firing pg_notify
per insert therefore caps enqueue throughput no matter how much hardware is
available. The 1 s cron tick is the batching mechanism that avoids it: at most
one notification per queue per second regardless of how many jobs arrive, so a
batch enqueue of a thousand jobs produces a single wake.
The poll is still the durable path
ergon.jobs is the durable fact and checkout's FOR UPDATE SKIP LOCKED is the
reliable puller, so a notification is a hint that cannot be depended on. Workers
keep their periodic fallback poll, which alone drains everything correctly. That
fallback covers the gap before the listener connects, every reconnect window, and
every deployment without pg_cron, where the tick never runs and no notification
ever fires. A dropped notification costs latency, never a stuck job.
Because the tick is level-triggered on "runnable work exists" rather than edge-
triggered on an insert, it keeps re-waking workers until a queue is drained, and
a future-scheduled retry wakes its workers on the first tick after scheduled_at
passes, precisely when it is due.
Configuration
Optional. Disabled, workers simply poll: still fully correct, only slower.
{ergon, [{ergon_job_notifier, [{enabled, false}]}]}A note on connection poolers
LISTEN needs a stable, dedicated backend, which is why ergon_listener's
connection sits outside the pool. But if the PG* environment points at a
transaction-mode pooler such as PgBouncer, that dedicated connection still goes
through the pooler and LISTEN silently never receives anything. Point Ergon at
a session-mode route when a transaction-mode pooler sits in front of PostgreSQL.
Summary
Functions
The single LISTEN/NOTIFY channel job wake-ups travel on.
Whether the host has left the reactive fast path enabled. Defaults to true.
Types
-type opts() :: #{config => pgo:pool_config()}.