Repository navigation
Perf Baseline
npm run perf:lts-baseline (scripts/perf/lts-baseline.mjs) records one reproducible
performance snapshot of the current tree: heap behaviour, routing hot-path cost, release
build time and HTTP time-to-first-byte. It was added on the 3.9.0 LTS rail (3.8.56) so the
LTS cut has a baseline to compare against instead of impressions.
It does not replace the existing probes. It runs them in a fixed order, isolates them, and writes a single JSON document (plus an optional Markdown table) that two runs can be diffed against.
Every number in the report depends on the machine and on what else that machine was doing. A shared dev box with dozens of worktrees and other sessions inflates timings by 2-3x compared with an idle host, and the inflation is not stable from run to run.
- Compare a report only with reports whose
hostblock matches (cpuCount,totalMemMB,nodeVersion) and whoseloadAvgwas similar. - The Markdown output prints a
host busywarning when the 1-minute load is above 25% of the CPU count. A report carrying that warning is preliminary and must not be used as a regression threshold. - The report deliberately omits the hostname. Record which box you used in the PR or note that publishes the numbers.
# Shared dev box — build is never run here (default), TTFB only if dist/ already exists
npm run perf:lts-baseline -- --runs 20 --out baseline.json --md > baseline.md
# Idle dedicated host — measures the release build and then TTFB on the fresh bundle
npm run perf:lts-baseline -- --with-build --runs 50 --out baseline.json --md > baseline.md
# Against a server that is already running (boot time is not measured)
npm run perf:lts-baseline -- --url http://127.0.0.1:20128 --runs 50 --out baseline.json| Flag | Meaning |
|---|---|
--skip-build |
Default. Do not run npm run build:release. Required on a shared dev box. |
--with-build |
Run build:release first and record its wall time. Use only on an idle host. Mutually exclusive with --skip-build. |
--runs <n> |
Timed requests per TTFB endpoint, after 3 discarded warmup requests (default 20). |
--url <base> |
Bench an already-running server instead of booting one. |
--out <path> |
Write the JSON report to that path. |
--md |
Print the Markdown table to stdout. Progress logs always go to stderr. |
--data-dir <path> |
Parent directory for the isolated per-measurement DATA_DIRs (default: a fresh temp dir, removed at the end). |
--heap-timeout-min <n> |
Abort test:heap after n minutes and mark it skipped (default 15). |
A failed or skipped measurement is data, not a crash: the script always writes the report and
exits 0. Only a bad flag exits non-zero (2). The script spawns npm without a shell, so it
targets Linux and macOS.
- Every child process gets its own
DATA_DIRunder--data-dir. The inheritedDATA_DIR(and therefore~/.omniroute) is never used, so the run cannot open or migrate an operator database. - Children get fake
JWT_SECRET/API_KEY_SECRET/INITIAL_PASSWORD,DISABLE_SQLITE_AUTO_BACKUP=true,REQUIRE_API_KEY=false, and no inheritedOMNIROUTE_API_KEY. - Commands are spawned with argument arrays (no shell interpolation) in their own process group. A timed-out command and the booted server are always killed, including on error.
The JSON shape is { generatedAt, host, gitSha, options, measurements }. Each measurement is
{ status: "ok" | "skipped" | "failed", durationMs, summary, raw? }.
| Measurement | Source | What it tells you |
|---|---|---|
buildSeconds |
npm run build:release |
Wall time of the full release build (Next standalone + CLI bundle). Only with --with-build. |
heapGrowth |
npm run test:heap |
raw.growthMB: V8 heap growth after 500 SSE streams through createSSEStream, measured after GC. A leak shows up as growth that scales with stream count. The test itself fails at 20 MB. |
heapBody |
npm run bench:heap-body -- --json |
raw.perRequestMiB: heap retained per request for the #7847 incident body shape (raw.wireMiB on the wire); raw.amplification is retained ÷ wire size. |
routingEvents |
npm run bench:routing-events |
raw.scenarios[].usPerOp: microseconds per routing decision, without and with the routing-event sinks. Relative cost matters more than the absolute value. |
serverBoot |
omniroute serve over dist/
|
Time from spawn to the first 200 from /api/health/ping (cold start). skipped with --url or without a built bundle. |
ttft |
/api/health, /v1/models, /api/monitoring/health
|
Per endpoint, time until the response headers arrive (time to first byte), sequential requests on a keep-alive connection: raw.medianMs, raw.p95Ms (nearest rank), raw.minMs, raw.maxMs. Non-2xx marks the endpoint failed. |
ttft needs a server. When no --url is given and dist/server.js does not exist, every
ttft entry is skipped with the reason: omniroute serve cannot boot an unbuilt tree.
When the bundle exists, the script boots it with NODE_ENV=production on an ephemeral port.
The baseline that counts for the LTS cut must come from an idle, dedicated host — see
RUNNER_BOX.md for the .113 pool and its scheduling limits.
-
Make sure no CI job or other build is running on the box (
uptimeload well below the CPU count; the report recordsloadAvgeither way). -
Check out the exact commit to measure and install with the lockfile:
npm ci. -
Run with the build:
npm run perf:lts-baseline -- --with-build --runs 50 \ --data-dir /var/tmp/omniroute-perf --out perf-<version>.json --md > perf-<version>.md
-
Run it a second time without
--with-build(the bundle now exists) and check that the TTFB medians of the two runs agree within a few percent. If they do not, the host was not idle. -
Publish both files with the host name and the date. Future comparisons use the same box and the same
--runs.
-
QUALITY_GATES.md: thenightly-resilienceworkflow runs the heap-growth gate on a schedule. -
bin/cold-start-bench.sh: a budget check (listen ≤ 800 ms, warm TTFB ≤ 200 ms) for a single boot, as opposed to this recorded baseline.
OmniRoute · Website · npm · Docker Hub
- Setup Guide
- User Guide
- Features
- Quick Start (Docker)
- Electron Desktop App
- Termux (Android)
- PWA Guide
- MCP Server
- A2A Server
- Agent Protocols
- OpenCode Plugin
- Webhooks
- Cloud Agents
- Skills
- Memory
- Evals
- Gamification
- Guardrails
- Compliance
- Error Sanitization
- Public Credentials
- Route Guard Tiers
- Stealth Guide
- CLI Token Auth