Benchmarks
Measured sender throughput and send() latency, the harness that produced them, and how to reproduce them.
The sender spec's Worker throughput target and Send latency budget requirements are benchmarked numbers, not aspirational ones — they're re-measured by the harness below and the spec is amended whenever reality moves.
Reference setup
- 1 Postgres node —
postgres:16-alpine, started via testcontainers, no tuning beyond the image default. - 4 in-process workers —
InProcess({ concurrency: 4 }), the TS reference concurrency mechanism. - A local mock HTTP receiver — returns
200immediately, so the numbers isolate the sender path rather than a receiver's processing time.
Machine assumptions
Numbers below were measured with Postgres running inside a Docker Desktop VM constrained to 2 vCPUs / 2.8 GB RAM (docker info), driven by a Node 20 process on the host. This is a modest, easily-reproduced machine, not dedicated benchmark hardware — the point of publishing is an honest floor, not a marketing ceiling. Re-run mise run bench on your own hardware; expect different absolute numbers and roughly the same shape.
Results
Delivery throughput
Sustained deliveries/sec, 4 in-process workers draining an outbox of 20,000 enqueued events against the mock receiver:
| Metric | Measured | Spec floor |
|---|---|---|
| Sustained deliveries/sec | ~360–400 (author's machine); ~140–150 (GitHub Actions shared runner) | ≥ 100 |
The spec floor (100/sec) is set with headroom below the slowest CI runner observed so far — it is a contractual minimum, not the expected number.
send() latency
Added latency of send() itself, firing 10,000 concurrent calls against a single Postgres node through a bounded 20-connection pool:
| Percentile | Measured |
|---|---|
| p50 | ~1.2 s |
| p95 | ~1.4–1.5 s |
| p99 | ~1.4–1.5 s |
| Metric | Spec ceiling |
|---|---|
| p99 added latency | ≤ 2000 ms |
A literal burst of 10,000 simultaneous calls queues through whatever connection pool it's given — at 20 connections, most of that queue is wait time, not insert time. This is expected: the number describes queuing behavior under a full-burst load, not the cost of a single unblocked insert.
Reproduce it
mise run benchRequires Docker (for the Postgres testcontainer). Runs both the throughput and latency scenarios and prints a report; set BENCH_JSON_OUT=<path> to also write it as JSON, or BENCH_LATENCY_SENDS / BENCH_THROUGHPUT_SENDS to change the sample sizes.
The source lives at typescript/scripts/bench.mjs. The throughput floor is additionally gated in CI via a POSTEL_PG_TESTCONTAINERS-only test at typescript/packages/storage/pg/test/throughput-benchmark.test.ts.