mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-15 06:52:04 +00:00
v1.4.37.1 — event-loop unblock on the read path
Hotfix on top of v1.4.37. The post-deploy verify caught /api/analytics cold-mount hits of ~62 s and parallel /api/health checks of ~58 s on the live instance, far above the v1.4.37 1.5-3 s perf claim. Root cause: the synchronous ensureUserRollupsFresh(userId) call at the top of each heavy read path folds the trailing 90-day DAY window when the rollup watermark trails the newest measurement. On a power-user account whose iOS step samples push the watermark every few minutes, each cache miss synchronously paid the full refresh cost on a Node worker that also serves the rest of the request fan-out (including /api/health, /api/version, and concurrent iOS calls), and the event loop was blocked for the duration of the recompute. Convert the three call sites (analytics route, comprehensive aggregator, summaries slice) to fire-and-forget. The downstream probeRollupCoverage gate falls back to live SQL on partial coverage so correctness is preserved; the only user-observable change is one cache window of staleness on the very first request after a new measurement landed.
This commit is contained in:
1 parent
88028fa082
commit
98e078d359
7 files changed
+161
-22
No files matched your search
@@ -179,11 +179,14 @@ export interface SummariesSlice {
|
||||
export async function computeSummariesSlice(
|
||||
userId: string,
|
||||
): Promise<SummariesSlice> {
|
||||
// Keep the persistent rollup table current before the bucket read
|
||||
// fires. On a warm process this is a single watermark query; on a
|
||||
// cold mount it folds the trailing 90-day window into the DAY
|
||||
// rollup so the bucket read below has something to compose.
|
||||
await ensureUserRollupsFresh(userId);
|
||||
// v1.4.37.1 hotfix — fire-and-forget. See `src/app/api/analytics/route.ts`
|
||||
// for the full rationale: awaiting this on the read path can stall
|
||||
// the event loop for 30–60 s on power-user accounts whose iOS step
|
||||
// samples keep the 90-day window slightly stale. The downstream
|
||||
// coverage probe falls back to live SQL for uncovered types, so
|
||||
// correctness is preserved; the read just doesn't block waiting
|
||||
// for the background refresh.
|
||||
void ensureUserRollupsFresh(userId);
|
||||
|
||||
// v1.4.36 QA C1 — per-type coverage probe replaces the legacy global
|
||||
// COUNT. The previous gate returned true as soon as ANY type had at
|
||||
|
||||
@@ -233,12 +233,14 @@ export async function buildComprehensiveAggregate(
|
||||
): Promise<ComprehensiveAggregate> {
|
||||
const ninetyDaysAgo = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000);
|
||||
|
||||
// Keep the persistent rollup table current before any read fires.
|
||||
// After this returns the DAY-granularity buckets for the trailing
|
||||
// 90 days reflect every measurement that landed before this call
|
||||
// (the per-write hook is synchronous on the DAY bucket, and
|
||||
// `ensureUserRollupsFresh` covers cold-mount / process-restart).
|
||||
await ensureUserRollupsFresh(userId);
|
||||
// v1.4.37.1 hotfix — fire-and-forget. See `src/app/api/analytics/route.ts`
|
||||
// for the full rationale: awaiting this on the read path can stall
|
||||
// the event loop for 30–60 s on power-user accounts whose iOS step
|
||||
// samples keep the 90-day window slightly stale. The downstream
|
||||
// coverage probe falls back to live SQL for uncovered types, so
|
||||
// correctness is preserved; the read just doesn't block waiting
|
||||
// for the background refresh.
|
||||
void ensureUserRollupsFresh(userId);
|
||||
|
||||
// v1.4.36 QA C1 — per-type coverage probe. The legacy global COUNT
|
||||
// returned true as soon as any one type had a DAY bucket, which made
|
||||
|
||||
Reference in new issue
Block a user