From c1a52c3268d4bcb0e61de080a0698a7298cd06df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sat, 16 May 2026 10:46:49 +0200 Subject: [PATCH] perf(dashboard): bound glucose + bp-in-target windows and stale-time the inline queries Three small fixes that compound on a power-user dashboard mount: - **H2**: The per-context glucose summaries walked every persisted `BLOOD_GLUCOSE` row a multi-year user has written. The summaries only feed the trailing tile, so bound the read to the last 30 days. - **M1**: The BP-in-target chunked walk passed no `since` bound even though `computeBpInTargetWindows`'s longest sub-window is `priorYear`. Pass a 365-day floor through both `fetchMeasurementSeriesChunked` calls. - **M5**: The three inline dashboard `useQuery` blocks shared no `staleTime` / `refetchOnWindowFocus` settings, so a tab-focus- and-return triggered an immediate refetch storm. Lift a shared `DASHBOARD_QUERY_OPTS` object so every query inherits the 1-minute staleness window the chart queries already use. iOS-neutral. No contract change; the analytics response shape stays identical. --- src/app/api/analytics/route.ts | 27 ++++++++++++++++++++++++--- src/app/page.tsx | 13 +++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/app/api/analytics/route.ts b/src/app/api/analytics/route.ts index 6069028b3..c18cce919 100644 --- a/src/app/api/analytics/route.ts +++ b/src/app/api/analytics/route.ts @@ -183,9 +183,21 @@ export const GET = apiHandler(async () => { // `BpReading[]` shape the existing helper expects. The // `analytics.bp_in_target.row_count` wide-event meta lets ops // attribute slow requests to specific outlier users. + // v1.4.29 M1 — bound the BP-in-target reads to the trailing + // 365 days. `computeBpInTargetWindows` only needs the last + // year (the longest sub-window it computes is `priorYear`); + // pre-bound, each chunked walk pulled the entire BP history + // every dashboard mount. + const bpInTargetSince = new Date( + now.getTime() - 365 * 24 * 60 * 60 * 1000, + ); const [sysData, diaData] = await Promise.all([ - fetchMeasurementSeriesChunked(user.id, "BLOOD_PRESSURE_SYS"), - fetchMeasurementSeriesChunked(user.id, "BLOOD_PRESSURE_DIA"), + fetchMeasurementSeriesChunked(user.id, "BLOOD_PRESSURE_SYS", { + since: bpInTargetSince, + }), + fetchMeasurementSeriesChunked(user.id, "BLOOD_PRESSURE_DIA", { + since: bpInTargetSince, + }), ]); annotate({ @@ -210,8 +222,17 @@ export const GET = apiHandler(async () => { } // Per-context glucose summaries (canonical mg/dL). + // + // v1.4.29 H2 — bound to the trailing 30 days. The dashboard tile + // path only reads the trailing window; pre-bound this query + // walked every persisted BG row a multi-year user has written. + const glucoseSince = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); const glucoseRows = await prisma.measurement.findMany({ - where: { userId: user.id, type: "BLOOD_GLUCOSE" }, + where: { + userId: user.id, + type: "BLOOD_GLUCOSE", + measuredAt: { gte: glucoseSince }, + }, orderBy: { measuredAt: "asc" }, select: { value: true, measuredAt: true, glucoseContext: true }, }); diff --git a/src/app/page.tsx b/src/app/page.tsx index 898067460..9a91e9904 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -191,6 +191,16 @@ export default function DashboardPage() { useState(null); const [moodFooterEl, setMoodFooterEl] = useState(null); + // v1.4.29 M5 — the three inline dashboard queries default to a + // 0-ms `staleTime`, so a tab-focus-and-return triggered a refetch + // storm. None of these are real-time data; minute-scale staleness + // is fine and the dashboard's chart queries already match this + // cadence. + const DASHBOARD_QUERY_OPTS = { + staleTime: 60_000, + refetchOnWindowFocus: false, + } as const; + const { data } = useQuery({ queryKey: queryKeys.analytics(), queryFn: async () => { @@ -200,6 +210,7 @@ export default function DashboardPage() { return json.data as AnalyticsData; }, enabled: isAuthenticated, + ...DASHBOARD_QUERY_OPTS, }); const { data: layoutData } = useQuery({ @@ -211,6 +222,7 @@ export default function DashboardPage() { return json.data as DashboardLayout; }, enabled: isAuthenticated, + ...DASHBOARD_QUERY_OPTS, }); const { data: moodData } = useQuery({ @@ -225,6 +237,7 @@ export default function DashboardPage() { }; }, enabled: isAuthenticated, + ...DASHBOARD_QUERY_OPTS, }); // v1.4.27 B1 — the dashboard's `` retired (it