Guide

Architecture

A supervisor forks workers that move messages through PGMQ's tables; the dashboard reads the same tables.

The shape of it#

Everything an app does — enqueue a job, publish an event, broadcast an update — goes through one door, Pgbus::Client, into PGMQ's queue tables. A supervisor forks workers that read and execute; a dispatcher runs the periodic maintenance; the dashboard reads the very same tables.

Everything goes through Pgbus::Client into PGMQ's tables; workers read and execute, the dashboard reads the same tables.

The layers#

From configuration up to the dashboard.

pgbus is organized as a stack — each layer depends only on the ones below it. You interact with the top; the invariants live at the bottom.

LayerWhat it does
DashboardThe mounted Rails engine — queues, jobs, failures, dead letters.
CLIpgbus start
Process modelSupervisor, worker, dispatcher, consumer, outbox poller.
Event busPublisher, subscriber registry, topic routing, idempotent handlers.
ActiveJobThe adapter + executor that serialize and run jobs.
ClientPgbus::Client
ConfigurationEvery knob, resolved once at boot.
The invariant that keeps the stack honest: all PGMQ access goes through Pgbus::Client — queue prefixing, visibility timeouts, and dead-letter routing are enforced there, not scattered across callers.

The processes a supervisor manages#

pgbus start boots a supervisor (a fork manager). It supervises:

ProcessResponsibility
WorkerReads a queue (or wakes via LISTEN/NOTIFY), executes jobs, recycles itself.
DispatcherMaintenance: idempotency cleanup, archive compaction, stale-process reaping, the circuit breaker.
SchedulerEnqueues recurring tasks on their cron schedule.
ConsumerReads event-bus topic queues and runs handlers.
Outbox pollerMoves committed outbox rows into PGMQ (when the outbox is enabled).

How a job flows through the system#

  1. Enqueue — Active Job serializes the job to JSON; pgbus sends it to the appropriate PGMQ queue through Pgbus::Client.
  2. Read — a worker polls the queue (or wakes instantly via LISTEN/NOTIFY) and claims the message with a visibility timeout, so no other worker takes it.
  3. Execute — the job is deserialized and run inside the Rails executor.
  4. Archive or retry — on success the message is archived to the a_<queue> table; on failure the visibility timeout expires and the message becomes available again. PGMQ's read_ct counts delivery attempts.
  5. Dead letter — when read_ct exceeds max_retries, the message moves to the <queue>_dlq queue for inspection instead of retrying forever.

The message lifecycle has its own picture in Retries & dead letters.