PgQue’s primary benchmark goal is not peak throughput — it is demonstrating zero-bloat behavior under the conditions that cause SKIP LOCKED queues to fail in production: a blocked xmin horizon.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.
Zero-bloat under blocked xmin horizon
The most common queue failure mode in production is not throughput degradation in a clean lab — it is silent degradation when xmin is held by a long-running transaction, an idle logical replication slot, orhot_standby_feedback=on with a slow replica. Under those conditions, VACUUM cannot reclaim dead tuples, the queue table bloats, and read latency climbs.
Test setup: PostgreSQL 17, 4 producers + 4 consumers + 2 bystander clients on an unrelated 1M-row table, 800 enqueues/sec, 3-minute runs, aggressive autovacuum (autovacuum_vacuum_scale_factor = 0.005, autovacuum_naptime = 10s).
| Scenario | Workload | Dequeue (jobs/s) | Dead tuples | Table size | Bystander avg latency |
|---|---|---|---|---|---|
| Baseline | SKIP LOCKED | 797 | 6,397 | 1.0 MiB | 1.35 ms |
| Baseline | PgQue | 792 | 0 | 13.4 MiB (live events) | 1.50 ms |
| RR holds xmin | SKIP LOCKED | 517 | 91,593 | 15.1 MiB | 2.05 ms |
| RR holds xmin | PgQue | 804 | 0 | 27.0 MiB (live events) | 1.45 ms |
- SKIP LOCKED dead tuple count grows 14× (6,397 → 91,593)
- SKIP LOCKED table size grows 15× (1.0 → 15.1 MiB)
- SKIP LOCKED dequeue throughput drops ~35% (797 → 517 jobs/sec)
- SKIP LOCKED bystander latency on an unrelated table rises ~50% (1.35 → 2.05 ms)
- PgQue:
n_dead_tup = 0across all event tables in every cell, throughput and bystander latency unchanged
PgQue’s live event table size (27 MiB) is larger than SKIP LOCKED because events are not deleted — they accumulate until the rotation TRUNCATE. This is expected and bounded by the rotation period.
benchmark/xmin-horizon/.
Steady-state throughput
Preliminary results on Apple Silicon (10 cores, 24 GiB RAM, PostgreSQL 18,synchronous_commit=off):
| Scenario | Throughput | Per core |
|---|---|---|
| PL/pgSQL single insert/tx, ~100 B, 16 clients | 85,836 ev/s | ~8.6k ev/s |
| PL/pgSQL batched 100k/tx, ~100 B | 80,515 ev/s | ~8.1k ev/s |
| PL/pgSQL batched 100k/tx, ~2 KiB | 48,899 ev/s (91.5 MiB/s) | ~4.9k ev/s |
| Consumer read rate, 100k batch, ~100 B | ~2.4M ev/s | ~240k ev/s |
| Consumer read rate, 100k batch, ~2 KiB | ~305k ev/s (568 MiB/s) | ~30.5k ev/s |
- Zero bloat verified — a 30-minute sustained test: zero dead-tuple growth in event tables
- Batching matters — throughput jumps when you stop doing one tiny transaction per event
- Consumer side is not the bottleneck — reads are much faster than writes
- Full Postgres guarantees — transactional semantics, WAL durability, SQL introspection
Methodology notes
These are preliminary numbers on specific hardware configurations. Postgres queue benchmarking is hard:synchronous_commit=offis used for maximum throughput; production settings will vary- Results depend heavily on hardware, Postgres config, and workload shape
- The xmin horizon test is more representative of production conditions than peak throughput numbers
benchmark/ in the repository. Numbers there are for reference and exploration, not a final verdict.