mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-15 06:52:04 +00:00
perf(charts): bound health-chart fetches to the active range window
The chart-data query in health-chart.tsx walked the full measurement history with a `while (true)` paginated loop on every visit. For Apple-Health-rich accounts (pulse every minute, months of backfill) this meant tens of thousands of rows per type per page navigation, which is the root cause behind FB-D2's "pulse insights loads forever" report. Derive `from` / `to` from the active range tab (plus the comparison-baseline shift when overlay is on), pass both to /api/measurements, and drop the unbounded paginator. The query key re-keys on the window so a range-tab change refetches the right slice. staleTime 60s + gcTime 5min hold the bounded payload across tab navigation between insights sub-pages. New `aggregate` query param + range-aggregation helper let the GET handler downsample server-side for ranges over 90 d (one row per day) and over 365 d (one row per ISO week). The per-request `limit` ceiling lifts from 500 to 5000 — still bounded, but enough headroom for an explicit from/to window without re-introducing pagination. iOS contract — GET /api/measurements: additive. The `from`, `to`, `aggregate` params are all optional; callers that omit them (the iOS client, the existing measurements page) see the unchanged response shape and the historical 500-row default cap. The aggregated branch is only entered when both from + to are present AND the window is wide enough for the threshold ladder to engage.
This commit is contained in:
1 parent
538b44f74d
commit
b00be2863d
6 files changed
+471
-47
No files matched your search
@@ -242,13 +242,21 @@ export const listMeasurementsSchema = z.object({
|
||||
.datetime({ offset: true })
|
||||
.transform((s) => new Date(s))
|
||||
.optional(),
|
||||
limit: z.coerce.number().int().min(1).max(500).optional().default(100),
|
||||
// v1.4.28 FB-D2 — when the chart sends an explicit from/to window the
|
||||
// payload is already bounded; lift the per-request ceiling to 5000
|
||||
// (still cheaper than the legacy unbounded `while (true)` walk).
|
||||
// Callers that omit from/to keep the historical 500-row cap.
|
||||
limit: z.coerce.number().int().min(1).max(5000).optional().default(100),
|
||||
offset: z.coerce.number().int().min(0).optional().default(0),
|
||||
sortBy: z
|
||||
.enum(["type", "value", "measuredAt", "source"])
|
||||
.optional()
|
||||
.default("measuredAt"),
|
||||
sortDir: z.enum(["asc", "desc"]).optional().default("desc"),
|
||||
// v1.4.28 FB-D2 — bucket hint for range-aware queries. When set the
|
||||
// GET handler downsamples server-side via `aggregateRows` and returns
|
||||
// one row per bucket per type rather than the raw measurement rows.
|
||||
aggregate: z.enum(["raw", "daily", "weekly"]).optional(),
|
||||
});
|
||||
|
||||
export const createBatchMeasurementSchema = z.object({
|
||||
|
||||
Reference in new issue
Block a user