“Queue latency” in PgQue is not one number — it is three. Understanding which number matters for your workload determines whether PgQue is the right fit, and how to configure the tick rate.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/NikolayS/PgQue/llms.txt
Use this file to discover all available pages before exploring further.
Three latencies
| Latency | What it measures | Typical value |
|---|---|---|
| Producer latency | Time for send() / insert_event() to complete | Sub-millisecond |
| Subscriber latency | Time for receive() over a pre-built batch | Sub-millisecond |
| End-to-end delivery | Time from send() to consumer visibility | ≈ tick period (default ~50 ms median) |
send() is a simple row insert, and receive() reads from a pre-materialized batch — no row-level locking or conflict resolution.
End-to-end delivery depends on the tick cadence. With the default 100 ms tick period, a consumer polls within at most 100 ms of the tick completing, so median end-to-end delivery is roughly 50 ms.
Tick cadence trade-offs
| Tick period | Ticks/sec | Median e2e latency | WAL/day (per active queue) |
|---|---|---|---|
| 1 ms | 1000 | ~0.5 ms | ~24 GiB |
| 10 ms | 100 | ~5 ms | ~2.4 GiB |
| 50 ms | 20 | ~25 ms | ~480 MiB |
| 100 ms (default) | 10 | ~50 ms | ~240 MiB |
| 200 ms | 5 | ~100 ms | ~120 MiB |
| 500 ms | 2 | ~250 ms | ~48 MiB |
| 1000 ms | 1 | ~500 ms | ~24 MiB |
Idle queue behavior
The WAL estimates above are for queues with continuous producer activity. Idle queues are cheap. When no events arrive, mostticker() calls return NULL — no tick is materialized. PgQue backs off toward ticker_idle_period (default 1 minute), so an inactive queue produces only occasional metadata writes rather than the full WAL budget.
Side effects of high tick rates
Higher tick rates increase more than just WAL: NOTIFY rate.pgque.ticker() emits pg_notify('pgque_<queue>', ...) on each materialized tick. Postgres’s NOTIFY queue is global (8 GiB SLRU); slow LISTEN consumers can fall behind at very high rates.
Metadata-table churn. pgque.tick and pgque.subscription are updated on every tick. At sub-50 ms tick periods, drop the rotation period proportionally to keep dead-tuple peaks bounded.
pg_cron worker pool. ticker_loop() holds one pg_cron background worker for ~1 second per slot. With pg_cron’s default cron.max_running_jobs = 32, this limits roughly 30 pgque-bearing databases per Postgres cluster. Not a per-database concern, but relevant when running many databases on one instance.
