Performance & load testing
Measure throughput and latency of an install, results, and what to scale when.
scripts/load-test.mjs measures a running Diffbridge: concurrent API reads (signed in, the pages
people open most), the deep health check, and a burst of comparisons for the background worker. It
creates its own throwaway workspace with the simulated demo orgs, so run it against a test install
in demo mode, never production.
RF_URL=http://127.0.0.1:8080 node scripts/load-test.mjs --seconds 30 --concurrency 20 --comparisons 10
Results on a developer laptop (2026-11-05)
npm start (API and worker in one process, embedded PGlite database, file storage), Windows 11,
Node 24:
| Load | Throughput | Latency p50 / p95 / p99 | Errors |
|---|---|---|---|
| 20 concurrent clients, 30 s | 113 requests/s | 175 / 294 / 487 ms | 0 |
| 50 concurrent clients, 15 s | 114 requests/s | 416 / 902 / 1125 ms | 0 |
| Worker burst | Finished | First / median / last |
|---|---|---|
| 10 comparisons started at once | 10 of 10 | 1.2 / 1.5 / 2.1 s |
| 20 comparisons started at once | 20 of 20 | 1.0 / 2.2 / 3.4 s |
Reading these: throughput stays at about 115 requests a second whatever the concurrency; only latency grows. The limit is PGlite, the embedded database used for local development. It runs Postgres in WebAssembly on one connection, so queries wait their turn. Production uses real Postgres with a connection pool, so these numbers are a floor, not the production capacity.
The first run found a real bug: under concurrent load the deep health check (/healthz?deep=1)
sometimes reported storage as down, because simultaneous checks wrote the same probe file. Checks
now share one run and use their own probe file. A regression test covers it.
Measuring production capacity
Run the same script on the production VM's twin (same shape, Postgres, Object Storage), against a copy of the stack in demo mode:
RF_URL=https://staging.example.com node scripts/load-test.mjs --seconds 60 --concurrency 50 --comparisons 20
While it runs, watch:
docker stats: CPU ofapi,worker,postgres;/metrics:rapidforge_http_request_duration_secondsby route,rapidforge_background_tasks;- Postgres:
select count(*) from pg_stat_activity(connections) and slow queries.
What to expect, and what to do about it:
- API latency rises first: run more API containers behind Caddy. They share nothing but Postgres.
- Comparisons and deployments wait in the queue:
docker compose up -d --scale worker=N. Each org still runs one deployment at a time; different orgs run in parallel. - Postgres CPU saturated: move to a bigger VM shape, or managed Postgres (Migrating to AWS).
- Real Salesforce orgs are usually the slowest part: metadata retrieves take seconds to minutes and are limited by Salesforce, not Diffbridge. Large orgs are retrieved in chunks of up to 10,000 files, three at a time.