perf(insights): route daily chart fetches through rollup buckets

The Insights trends row fires three parallel
GET /api/measurements?type=…&aggregate=daily&limit=5000 requests on
mount (BP_SYS / BP_DIA / WEIGHT). The legacy date_trunc path scanned
the measurements table each time — ~3 s per call on Marc's account.

Add a `source=rollup` opt-in on the listMeasurements schema. When set
alongside `aggregate=daily` the route reads from measurement_rollups
DAY buckets instead, swapping a full-table scan for a small indexed
read against the ~5 k-row rollup table. Cumulative HK metrics get
mean * count to reconstruct the daily SUM the chart expects.

The chart-data client opts in for every windowDays > 7 fetch so the
trends row + every sub-page chart benefit. When the rollup returns
zero buckets for the requested window the route falls through to the
live aggregate so brand-new accounts still render their first chart
correctly.
This commit is contained in:
Marc-André Bombeck committed 2026-05-17 12:34:00 +02:00
1 parent 6884995995
commit d2bad6943c
5 files changed
+182 -1

No files matched your search

+12
View File
@@ -585,8 +585,20 @@ export function HealthChart({
// Recharts paint cost ~50× on continuous-monitoring accounts.
// Short windows keep raw fetching so hour-by-hour detail
// stays visible.
//
// v1.4.36 W1 — also route the daily aggregate through the
// persistent rollup buckets via `source=rollup`. The route
// reads from `measurement_rollups` instead of running a live
// `date_trunc` scan over the raw measurements table; the
// three parallel daily fetches on the Insights trends row
// (BP_SYS / BP_DIA / WEIGHT) drop from ~3 s each to a small
// indexed read against the ~5 k-row rollup table. The server
// falls back to live SQL when the rollup is empty for the
// requested window so brand-new accounts still see a chart
// on their first render.
if (fetchWindow.windowDays > 7) {
typeParams.set("aggregate", "daily");
typeParams.set("source", "rollup");
}
const res = await fetch(`/api/measurements?${typeParams}`);