docs/performance.md,
which includes the full methodology and raw output so the numbers can be
reproduced and challenged.
Test setup (as published, 2026-07-06, around v0.1.1+): Apple M2 Pro
(10 cores, 16 GB), macOS, full
docker-compose stack (API + PostgreSQL 15 +
TigerBeetle connected + Mailhog), load generated with
hey from the same machine. Payment
gateways in mock mode — the numbers isolate Recurso + PostgreSQL, not
external gateway latency.Results
Ledger integrity under load: the 17,352 subscription creations produced
exactly 17,352 ledger transactions — verified by count and by the
reconciliation report showing zero
discrepancies for the post-fix window.
What the load test found (and fixed)
Running the test the first time surfaced three production bugs, all fixed in the same change series (shipped in 0.2.0):- Per-request bcrypt capped the API at ~126 req/s (p50 373 ms). A TTL’d verified-key cache (SHA-256 keyed, 5-minute expiry, so revocation still takes effect) took authenticated reads from 126 → 7,814 req/s and p99 from 773 ms → 27 ms.
- Only one tenant could register per database — a unique constraint on the always-empty hashed key column.
- Ledger postings failed for all API-created tenants/customers — AR and chart-of-accounts provisioning is now self-healing on first posting.
Backup / restore drill (verified)
Performed 2026-07-06 against the stack above with ~58k invoices and ~17k ledger transactions:pg_dump (43 MB), all volumes destroyed, restore
into a clean database — row counts identical across tenants, customers,
subscriptions, invoices, and ledger transactions; API keys still
authenticate; /health green.
Caveats
- Single-node everything, loopback networking, load generator on the same box — treat these as an upper bound for one instance, not a cluster benchmark.
- Mock gateways: real Stripe/Razorpay calls add their own latency to subscription creation.
- Rate limiting was raised via
RATE_LIMIT_PER_MINUTEfor the test; the default remains 500/min per key.