mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-15 15:02:04 +00:00
perf(analytics): read the latest value per type with one probe per type
The latest canonical reading per type ran as `DISTINCT ON (type)` over every live row of the account in five places (summaries slice warm and cold path, comprehensive aggregator warm and cold path, targets). With no skip scan in Postgres 16 that sorted the whole account to keep one row per type. `readLatestCanonical` drives the read from the type list instead: one index probe for each type's latest instant, then a closed one-day window ranked by the source ladder. The closed window also keeps the plan under `jit_above_cost`. The targets read reuses `readLatestEver` with a floor. The summaries slice was built inside three cache cells (the tile-strip route, the thick analytics body, the dashboard snapshot). It now has one cell, read through `readSummariesSlice`; composing callers read it fresh-only so a refresh after a write cannot cache the pre-write slice again, and the snapshot folds the water tile into a copy instead of the cached object.
This commit is contained in:
1 parent
8eb4ef3d56
commit
9c51dd0d30
14 files changed
+436
-185
No files matched your search
@@ -118,7 +118,7 @@ vi.mock("@/lib/analytics/correlations-fast-path", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/analytics/summaries-slice", () => ({
|
||||
computeSummariesSlice: vi.fn().mockResolvedValue({
|
||||
readSummariesSlice: vi.fn().mockResolvedValue({
|
||||
summaries: {},
|
||||
bmi: null,
|
||||
bpInTargetPct: null,
|
||||
@@ -176,7 +176,7 @@ beforeEach(async () => {
|
||||
await import("@/lib/analytics/score/reader");
|
||||
const { computeCorrelationHypothesesFastPath } =
|
||||
await import("@/lib/analytics/correlations-fast-path");
|
||||
const { computeSummariesSlice } =
|
||||
const { readSummariesSlice } =
|
||||
await import("@/lib/analytics/summaries-slice");
|
||||
|
||||
(ensureUserRollupsFresh as ReturnType<typeof vi.fn>).mockResolvedValue(
|
||||
@@ -196,7 +196,7 @@ beforeEach(async () => {
|
||||
(
|
||||
computeCorrelationHypothesesFastPath as ReturnType<typeof vi.fn>
|
||||
).mockResolvedValue({ hypotheses: [] });
|
||||
(computeSummariesSlice as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
(readSummariesSlice as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
summaries: {},
|
||||
bmi: null,
|
||||
lastSeenByType: {},
|
||||
@@ -206,7 +206,7 @@ beforeEach(async () => {
|
||||
describe("GET /api/analytics — per-type live walk retired (v1.4.49.1)", () => {
|
||||
// v1.4.49.1 — the W-SINCE 425-day `since` cap was a defense-in-depth
|
||||
// bound on the now-deleted 15-way per-type live walk. The default
|
||||
// slice routes through `computeSummariesSlice` exclusively, which
|
||||
// slice routes through `readSummariesSlice` exclusively, which
|
||||
// reads `measurement_rollups` DAY buckets + a 90-day narrow
|
||||
// `$queryRaw` directly — no chunked findMany against `measurements`
|
||||
// at all on the default critical path. The two assertions that pinned
|
||||
@@ -222,7 +222,7 @@ describe("GET /api/analytics — per-type live walk retired (v1.4.49.1)", () =>
|
||||
);
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
// The slim slice resolves through `computeSummariesSlice` only;
|
||||
// The slim slice resolves through `readSummariesSlice` only;
|
||||
// the per-type findMany loop is skipped entirely so the rollup-
|
||||
// path semantics are unaffected by the `since` cap.
|
||||
const findMany = prisma.measurement.findMany as ReturnType<typeof vi.fn>;
|
||||
|
||||
Reference in new issue
Block a user