Skip to main content

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.

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.

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, or hot_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).
ScenarioWorkloadDequeue (jobs/s)Dead tuplesTable sizeBystander avg latency
BaselineSKIP LOCKED7976,3971.0 MiB1.35 ms
BaselinePgQue792013.4 MiB (live events)1.50 ms
RR holds xminSKIP LOCKED51791,59315.1 MiB2.05 ms
RR holds xminPgQue804027.0 MiB (live events)1.45 ms
When xmin is blocked:
  • 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 = 0 across 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.
The reproducer and raw 5-second metrics are in benchmark/xmin-horizon/.

Steady-state throughput

Preliminary results on Apple Silicon (10 cores, 24 GiB RAM, PostgreSQL 18, synchronous_commit=off):
ScenarioThroughputPer core
PL/pgSQL single insert/tx, ~100 B, 16 clients85,836 ev/s~8.6k ev/s
PL/pgSQL batched 100k/tx, ~100 B80,515 ev/s~8.1k ev/s
PL/pgSQL batched 100k/tx, ~2 KiB48,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
Key takeaways:
  • 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=off is 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
For cross-system comparisons, see benchmark/ in the repository. Numbers there are for reference and exploration, not a final verdict.

Build docs developers (and LLMs) love