6 Commits
Author SHA1 Message Date
Marc-André Bombeck 8c43ff7cf9 fix(bench): move the heap snapshot and read the commit without a shell 2026-10-23 02:50:02 +02:00
Marc-André Bombeck a451a114a2 docs(bench): record the boot profile and the runtime results
The profile section in docs/contributing/benchmarks.md says how to run
scripts/bench/boot-profile.ts, what the profile of the v1.44 runtime
found (the worker graph as one 753 ms block, a live heap of about
165 MB of which the query compiler holds 50 and the catalogs 33) and what
changed. scripts/bench/results/v1.45.0-*.md hold the before and after
numbers per area, from back-to-back runs on the same machine. The size
baselines take the lowered numbers.
2026-10-22 09:10:14 +02:00
Marc-André Bombeck b5f5a5a580 perf(i18n): build the every-locale match indexes on first use
Seven matchers built their index over every shipped catalog at module
load (the GLP-1 tag map, the nutrient and metric names of the MCP reads,
the SNOMED and LOINC lab names, the Coach's health words, the lab-name
pattern), so loading the worker graph read all eight catalogs at boot.
Each index is now built the first time it is asked for.

The boot profile records the change: the longest busy stretch of the
main thread at boot fell from 753 ms to 241 ms, the live heap after five
idle minutes from 164 to 152 MB, and the catalog chunk sources (two of
9.7 MB) are gone.
2026-10-22 07:01:03 +02:00
Marc-André Bombeck 63e8630fcb perf(bench): profile a production boot and the idle heap
scripts/bench/boot-profile.ts starts the production build with every role
under the production heap ceiling against the seeded benchmark database,
and records the time to a healthy probe and to a ready worker, every
event-loop stall the process logs, the heap lines, the full collections
from --trace-gc, the main thread's busy stretches during the boot with
their self time by source module (resolved through the chunk source maps),
and one heap snapshot after an idle wait, summarised by node type,
constructor, largest strings and largest retained subtrees.

The baseline on the current runtime: a 753 ms busy stretch at 0.7 s, most
of it the worker graph, and a live heap of 164 to 172 MB after five idle
minutes, of which the query compiler holds about 50 MB and the eight
locale catalogs about 33 MB (32 parsed copies and two 9.7 MB chunk
sources).
2026-10-22 06:41:15 +02:00
Marc-André Bombeck 9bdd0b1a60 perf(bench): record the baseline on the v1.44.0 runtime
One run of scripts/bench/run.ts against the seeded account (1,266,549
measurement rows, anchor 2026-10-22), 20 measured requests per row after 3
warm-up requests, web-only production build, 792 MB heap ceiling, Postgres
16.15. The commit adds only lint rules, guards, tests and scripts on top of
v1.44.0, so the server code measured is the v1.44.0 code.

Cold (caches evicted before each request), p95 and statements per request:
/api/auth/me 17 ms / 19, dashboard summary 233 ms / 31, dashboard snapshot
2253 ms / 158, pulse series 30 d 59 ms / 8, 365 d 311 ms / 8, 3650 d
2118 ms / 8, day view 78 ms / 49, insights health status 140 ms / 68,
correlations 105 ms / 45. Warm, the snapshot is 24 ms / 13 and the 3650-day
series still 1506 ms, since that read is not cached. The 5,000-row batch
ingest took 1722 ms and 424 statements over ten requests.

The machine was shared with other work during the run (load average 8 to
20), so latency is indicative; the statement counts do not depend on it.
The results directory is written by the script and left out of prettier,
so a re-run diffs cleanly.
2026-10-22 05:55:38 +02:00
Marc-André Bombeck 375ca12b05 perf(bench): add a large-account seed and a request benchmark
scripts/bench/seed-large-account.ts (moved from scripts/, extended) writes
one synthetic account of ten years: 1,265,561 measurement rows across the
main types and five sources (Apple Health dense spot rows, Withings blood
pressure and scale, a weekly manual weight, three years of Oura nightly
values, 90 days of Nightscout CGM), `stats:` and per-sample cumulative rows,
nightly sleep stages, 3,020 workouts, 1,095 mood entries and three
medications with 3,285 intake events. Everything goes in through set-based
INSERT … SELECT generate_series; values and timestamps are functions of the
row index and `--anchor`, so a run is reproducible. The rollup tier is
folded with recomputeUserRollups, as the boot backfill does, and the tables
are analysed. About 80 s on the Mac mini.

scripts/bench/run.ts boots the production build once with every role (so
the queues exist), then measures against a web-only process under the
production heap ceiling: p50, p95 and max latency and SQL statements per
request (pg_stat_statements, read once nothing is in flight and the count
holds still) for /api/auth/me, the dashboard summary and snapshot, the pulse
series over 30, 365 and 3650 days, the day view, insights health status and
correlations, each cold (caches evicted by a one-row manual-sync ingest
before every request) and warm, plus a 5,000-row batch ingest as ten
requests of 500. It writes JSON and a markdown table.

docs/contributing/benchmarks.md describes the setup against a throwaway
Postgres in Docker with the production pg_stat_statements settings.
2026-10-22 05:38:36 +02:00