Live demo: a synthetic workload runs on a single PostgreSQL. Traffic comes in bursts, some jobs fail and retry, and every couple of minutes a worker is killed with SIGKILL to show lease recovery. This page is read-only.
Throughput–jobs / second
Pickup latency–p50 / p95 · due to claimed
Run time–p50 / p95 · handler
Waiting–queued + retrying
Running–holding a lease
Retries–last 60 s
Failed–last 60 s
Throughput · finished jobs per second, last two minutes
succeededfailed / dead
Queues · depth over the last two minutes
No jobs yet
Workers · heartbeats and leases
No workers running
Live job feed
Waiting for jobs…
Running jobs · each one holds a lease that its worker keeps extending
Job
Task
Queue
Worker
Attempt
Running
Lease left
Nothing is running right now
Problems
Job
Task
Queue
Attempts
Last error
When
Nothing here
Periodic schedules
Name
Cron (UTC)
Task
Queue
Next run
Last run
No schedules
How it works
Every job is a row in one PostgreSQL table. There is no broker: Postgres is the queue,
and three of its features do the heavy lifting.
Solid arrows are SQL statements, dashed arrows are LISTEN/NOTIFY wake-ups.
SKIP LOCKED claiming
Workers claim a batch of jobs with SELECT … FOR UPDATE SKIP LOCKED inside a single
UPDATE. Rows another worker already holds are skipped instead of waited on, so
any number of workers pull different jobs at once and never block each other.
UPDATE pgqueue_jobs SET status = 'running', …
FROM (SELECT id FROM pgqueue_jobs
WHERE status IN ('queued','retrying')
AND run_at <= now()
ORDER BY priority DESC, run_at, id
LIMIT 8
FOR UPDATE SKIP LOCKED) AS next
Leases and crash recovery
A running job holds a lease that its worker extends with heartbeats. If a worker dies,
its leases run out and a reaper in every other worker puts the jobs back with the attempt
counted. A result is accepted only from the current lease holder, so a stalled worker
cannot overwrite a newer one (fencing).
LISTEN / NOTIFY
A trigger sends NOTIFY on every insert, so idle workers wake in milliseconds
instead of waiting for the next poll. Polling stays as the fallback: a lost notification
costs latency, never a job.
Retries and dead letters
Failed attempts are retried with capped exponential backoff and jitter. A task may ask for a
specific delay (Retry-After) or fail permanently. Jobs that use up every attempt
end up dead and stay visible until someone requeues them.