title: Benchmarks

Benchmarks

Three connection poolers — pg_doorman, pgbouncer, odyssey — driven by pgbench against the same PostgreSQL backend on identical hardware. Numbers below are relative throughput against each competitor and absolute per-transaction latency.

Last updated: 2026-04-27 12:00 UTC.

TL;DR

  • vs pgbouncer — pg_doorman peaks at x12.0 TPS on prepared protocol, 120 clients.
  • vs odyssey — pg_doorman wins by +40% at most (extended protocol, 120 clients).
  • Tail spread at 10 000 simple-protocol clients (p99/p50, lower = more predictable) — pg_doorman 1.1× (59.9→64.5ms), pgbouncer 1.4× (276→387ms), odyssey 11× (17.9→204ms).

Tail spread at 10,000 simple-protocol clients

Environment

  • Provider: Ubicloud standard-60 (eu-central-h1)
  • Resources: 60 vCPU / 235.9 GB
  • Kernel: Linux 5.15.0-139-generic x86_64
  • Versions: PostgreSQL 14.22, pg_doorman 3.6.1, pgbouncer 1.25.1, odyssey 1.4.1
  • Workers: pg_doorman: 30, odyssey: 30
  • Duration per pgbench run: 60s
  • Started: 2026-04-27 08:06 UTC
  • Finished: 2026-04-27 11:03 UTC
  • Total wall-clock: 2h 57m 08s
  • Commit: c9dd765c

Methodology

Each scenario runs pgbench -T <duration> against a 40-connection server-side pool (pool_mode = transaction). The workload is a single SELECT :aid (\set aid random(1, 100000)) — pure pooler overhead, no real working set. Three poolers, one PostgreSQL backend, identical hardware.

  • Reconnect rows use pgbench --connect: a fresh TCP+startup per transaction (worst case for login latency).
  • SSL rows set PGSSLMODE=require and a self-signed cert.
  • Latency is collected with pgbench --log (per-transaction file); percentiles come from those samples, not from pgbench summary stats.
  • Scenarios run sequentially with the same data dir and warm OS caches.

Source: tests/bdd/features/bench.feature, driver: benches/setup-and-run-bench.sh.

Reading the tables

Throughputpg_doorman_TPS / competitor_TPS, rendered:

ValueMeaning
+N% / -N%Faster / slower by N percent
≈0%Within 3% — call it a tie
xN.NN times faster (when ratio ≥ 1.5)
Competitor returned 0 TPS
N/ACompetitor was not measured for this row
-Not measured for either pooler

Latency — per-transaction in ms. Each row shows p50 / p99 for every pooler plus the spread (p99 / p50): how far the slowest 1% drifts from the median. 1.0× means the tail equals the median; 100× means the worst 1% takes two orders of magnitude longer than a typical request — the regime where fanout latency starts hitting users (Dean & Barroso, 2013). Watch the spread column to see whether tail latency stays bounded as the client count grows. Full p95 series ships in the raw pgbench --log files in the artifact tarball.


Simple protocol

Simple protocol: latency p50 (solid) and p99 (dashed)

Throughput

Testvs pgbouncervs odyssey
1 client≈0%≈0%
40 clientsx2.9-46%
120 clientsx9.9≈0%
500 clientsx6.6-32%
10,000 clientsx4.7-34%
1 client + Reconnect-14%x2.0
40 clients + Reconnectx1.6N/A
120 clients + Reconnectx1.7N/A
500 clients + Reconnectx1.7N/A
10,000 clients + Reconnect+41%N/A
1 client + SSL≈0%≈0%
40 clients + SSLx3.1-38%
120 clients + SSLx8.5-5%
500 clients + SSLx10.6+18%
10,000 clients + SSLx7.1+12%
1 client + SSL + Reconnect-6%x1.6
40 clients + SSL + Reconnect≈0%-35%
120 clients + SSL + Reconnect+5%-39%
500 clients + SSL + Reconnect+17%-28%
10,000 clients + SSL + Reconnect-8%-16%

Latency (ms; spread = p99 / p50)

Testpg_doorman p50/p99spreadpgbouncer p50/p99spreadodyssey p50/p99spread
1 client0.08 / 0.101.4×0.07 / 0.101.4×0.07 / 0.121.7×
40 clients0.27 / 0.501.8×0.74 / 1.902.6×0.12 / 0.302.5×
120 clients0.29 / 0.913.2×2.86 / 6.772.4×0.24 / 2.078.8×
500 clients2.30 / 4.381.9×12.6 / 27.62.2×0.82 / 7.879.5×
10,000 clients59.9 / 64.51.1×276 / 3871.4×17.9 / 20411×
1 client + Reconnect0.14 / 0.231.6×0.11 / 0.211.9×0.18 / 0.311.7×
40 clients + Reconnect1.26 / 4.103.2×1.91 / 6.263.3×1.85 / 5.352.9×
120 clients + Reconnect3.83 / 11.12.9×5.89 / 18.13.1×5.95 / 16.72.8×
500 clients + Reconnect16.3 / 42.92.6×26.2 / 71.12.7×25.3 / 65.42.6×
10,000 clients + Reconnect369 / 7632.1×524 / 11062.1×744 / 15192.0×
1 client + SSL0.08 / 0.111.4×0.08 / 0.111.4×0.08 / 0.121.6×
40 clients + SSL0.27 / 0.501.8×0.87 / 2.162.5×0.15 / 0.281.9×
120 clients + SSL0.42 / 1.162.7×3.71 / 8.612.3×0.30 / 2.077.0×
500 clients + SSL1.09 / 2.542.3×17.0 / 34.72.0×1.04 / 5.425.2×
10,000 clients + SSL26.9 / 64.02.4×369 / 5111.4×29.7 / 82.42.8×
1 client + SSL + Reconnect0.23 / 0.361.6×0.19 / 0.351.9×0.28 / 0.431.5×
40 clients + SSL + Reconnect17.6 / 42.22.4×16.3 / 53.13.3×11.4 / 31.72.8×
120 clients + SSL + Reconnect57.0 / 1302.3×55.6 / 1663.0×33.9 / 95.72.8×
500 clients + SSL + Reconnect212 / 4832.3×237 / 6192.6×150 / 3922.6×
10,000 clients + SSL + Reconnect5033 / 100552.0×4052 / 102452.5×4361 / 86122.0×

Extended protocol

Extended protocol: latency p50 (solid) and p99 (dashed)

Throughput

Testvs pgbouncervs odyssey
1 client≈0%x1.6
40 clientsx3.0-21%
120 clientsx10.4+40%
500 clientsx7.4+7%
10,000 clientsx5.2≈0%
1 client + Reconnect-16%x1.9
40 clients + Reconnectx1.7N/A
120 clients + Reconnectx1.6x1.5
500 clients + Reconnectx1.7N/A
10,000 clients + Reconnect+41%x2.2
1 client + SSL≈0%+49%
40 clients + SSLx3.3-13%
120 clients + SSLx9.4+50%
500 clients + SSLx10.4x1.7
10,000 clients + SSLx7.1+25%

Latency (ms; spread = p99 / p50)

Testpg_doorman p50/p99spreadpgbouncer p50/p99spreadodyssey p50/p99spread
1 client0.07 / 0.111.4×0.07 / 0.101.4×0.11 / 0.181.6×
40 clients0.27 / 0.481.8×0.76 / 1.902.5×0.18 / 0.482.7×
120 clients0.28 / 0.893.2×3.06 / 6.872.2×0.35 / 3.5210×
500 clients2.08 / 3.981.9×12.8 / 27.72.2×1.55 / 13.28.5×
10,000 clients55.4 / 60.31.1×288 / 3871.3×52.2 / 3266.2×
1 client + Reconnect0.14 / 0.221.6×0.12 / 0.221.8×0.24 / 0.411.7×
40 clients + Reconnect1.25 / 4.023.2×1.96 / 6.443.3×1.85 / 5.483.0×
120 clients + Reconnect3.81 / 11.12.9×5.62 / 17.83.2×5.74 / 15.72.7×
500 clients + Reconnect16.5 / 44.62.7×26.5 / 72.32.7×27.1 / 72.22.7×
10,000 clients + Reconnect368 / 7642.1×511 / 11392.2×816 / 16572.0×
1 client + SSL0.09 / 0.111.2×0.08 / 0.121.5×0.12 / 0.201.7×
40 clients + SSL0.27 / 0.491.8×0.91 / 2.202.4×0.23 / 0.371.6×
120 clients + SSL0.38 / 1.052.8×3.68 / 8.692.4×0.57 / 2.624.6×
500 clients + SSL1.01 / 2.652.6×17.6 / 36.12.1×2.29 / 7.343.2×
10,000 clients + SSL27.7 / 65.92.4×384 / 5211.4×49.2 / 1523.1×

Prepared protocol

Prepared protocol: latency p50 (solid) and p99 (dashed)

Throughput

Testvs pgbouncervs odyssey
1 client≈0%≈0%
40 clientsx3.4-49%
120 clientsx12.0≈0%
500 clientsx8.5-29%
10,000 clientsx6.2-32%
1 client + Reconnect-9%x2.1
40 clients + Reconnectx1.6+48%
120 clients + Reconnectx1.7N/A
500 clients + Reconnectx1.8N/A
10,000 clients + Reconnect+40%x2.1
1 client + SSL+3%≈0%
40 clients + SSLx3.8-36%
120 clients + SSLx10.0≈0%
500 clients + SSLx12.7+19%
10,000 clients + SSLx8.6+12%

Latency (ms; spread = p99 / p50)

Testpg_doorman p50/p99spreadpgbouncer p50/p99spreadodyssey p50/p99spread
1 client0.07 / 0.101.4×0.07 / 0.101.4×0.07 / 0.111.6×
40 clients0.27 / 0.491.8×0.90 / 2.232.5×0.12 / 0.262.2×
120 clients0.30 / 0.943.2×3.82 / 8.202.1×0.23 / 1.486.3×
500 clients2.21 / 4.231.9×16.5 / 33.02.0×0.81 / 6.938.6×
10,000 clients57.5 / 62.91.1×353 / 4651.3×17.5 / 20512×
1 client + Reconnect0.20 / 0.331.6×0.20 / 0.331.7×0.37 / 0.611.6×
40 clients + Reconnect1.84 / 5.202.8×2.67 / 8.543.2×2.73 / 7.352.7×
120 clients + Reconnect5.25 / 14.62.8×8.11 / 25.03.1×7.72 / 21.62.8×
500 clients + Reconnect21.6 / 58.42.7×38.0 / 1012.7×33.3 / 82.82.5×
10,000 clients + Reconnect485 / 10312.1×672 / 14212.1×1026 / 22142.2×
1 client + SSL0.08 / 0.121.4×0.08 / 0.121.5×0.08 / 0.131.7×
40 clients + SSL0.26 / 0.481.8×1.02 / 2.592.5×0.15 / 0.271.8×
120 clients + SSL0.42 / 1.162.8×4.44 / 9.742.2×0.28 / 1.154.0×
500 clients + SSL1.08 / 2.602.4×21.5 / 41.01.9×1.06 / 5.355.0×
10,000 clients + SSL27.1 / 58.32.1×463 / 6091.3×29.8 / 86.22.9×

Caveats

  • 30 s per run is short by pgbench standards (the docs recommend minutes); expect ±5% variance between runs. Re-run for production decisions.
  • Single PostgreSQL backend, no replicas, no real working set — these numbers measure pooler overhead, not full-system throughput.
  • All three poolers use vendor defaults plus pool_size = 40. Tuning specific knobs (pgbouncer so_reuseport, odyssey workers) will move the curves.
  • Reconnect is the worst-case login-latency scenario; the headline numbers in production rarely look like the Reconnect rows.
  • Workload is a 1-row SELECT. Read-heavy OLTP, OLAP, or LISTEN/ NOTIFY paths are not represented.