refactor(rollups): retire v1.4.39 W-SUM legacy-NULL UNION discovery arm

The `sum_value IS NULL` UNION arm in `enqueueBootTimeRollupBackfill`
was added in v1.4.39 W-SUM to converge DAY rollup rows that pre-dated
the writer change. Production data has since converged, so the arm is
a permanent no-op seq scan over `measurement_rollups`. Per-day missing
coverage remains the sole discovery anchor.

Drops the legacy text-anchor unit test. The read-side `mean * count`
fallback in `/api/dashboard/summary` retains legacy-row readability
and stays covered by its dedicated route test.
This commit is contained in:
Marc-André Bombeck committed 2026-05-21 17:35:10 +02:00
1 parent a1bfc3b461
commit 9d2901aaf9
2 files changed
+19 -52

No files matched your search

@@ -543,30 +543,6 @@ describe("enqueueBootTimeRollupBackfill", () => {
expect(bossSend).not.toHaveBeenCalled();
});
// v1.4.39 W-SUM — the discovery query now unions in users whose
// existing DAY rollup rows carry `sum_value IS NULL`. Re-folding
// converges those rows because `persistRollupRows` always writes the
// new column on upsert; the union keeps the per-day missing-coverage
// gap (v1.4.39.1) and the legacy-NULL backfill on the same indexed
// pass.
it("includes the sum_value IS NULL branch in the discovery query", async () => {
getGlobalBossMock.mockReturnValue({ send: bossSend });
queryRaw.mockResolvedValueOnce([{ id: "user-with-legacy-rollups" }]);
bossSend.mockResolvedValueOnce("job-id");
const result = await enqueueBootTimeRollupBackfill();
expect(result).toEqual({ enqueued: 1, skipped: 0, error: null });
// The discovery SQL surfaces both per-day missing coverage AND
// legacy NULL sum_value rows under one UNION. The text-anchor is
// the only durable assertion at the unit level — the integration
// suite covers the planner shape on real Postgres.
const sqlParts = queryRaw.mock.calls[0][0] as TemplateStringsArray;
const sqlText = Array.isArray(sqlParts) ? sqlParts.join("?") : "";
expect(sqlText).toContain("sum_value");
expect(sqlText).toContain("UNION");
});
// v1.4.39.1 — the discovery anchor moved from per-type to per-day so
// accounts whose Withings sync / `/api/import` / admin restore wrote
// measurements without firing the rollup write hook get re-enqueued
+19 -28
View File
@@ -752,35 +752,26 @@ export async function enqueueBootTimeRollupBackfill(): Promise<{
// index path. The LEFT JOIN walks `measurement_rollups` against
// its composite primary key.
//
// v1.4.39 W-SUM — the discovery also surfaces users whose existing
// DAY rollup rows carry `sum_value IS NULL`. Those rows pre-date
// the v1.4.39 writer change; re-folding the user converges them
// because `persistRollupRows` always writes the new column on
// upsert. The union runs in one query so the per-day coverage gap
// and the legacy-NULL backfill share a single planner pass.
// v1.4.41 — the v1.4.39 W-SUM legacy-NULL `sum_value` UNION arm
// has been retired. Marc's tenant converged at v1.4.40, so the
// arm was a permanent no-op seq scan over `measurement_rollups`.
// Per-day missing coverage remains the sole discovery anchor.
const users = await prisma.$queryRaw<Array<{ id: string }>>`
SELECT DISTINCT id FROM (
SELECT DISTINCT mt."user_id" AS id
FROM (
SELECT DISTINCT
m."user_id",
m."type",
date_trunc('day', m."measured_at") AS bucket_start
FROM measurements m
WHERE m."deleted_at" IS NULL
) mt
LEFT JOIN measurement_rollups r
ON r."user_id" = mt."user_id"
AND r."type" = mt."type"
AND r."granularity" = 'DAY'
AND r."bucket_start" = mt."bucket_start"
WHERE r."bucket_start" IS NULL
UNION
SELECT DISTINCT r2."user_id" AS id
FROM measurement_rollups r2
WHERE r2."granularity" = 'DAY'
AND r2."sum_value" IS NULL
) discovery
SELECT DISTINCT mt."user_id" AS id
FROM (
SELECT DISTINCT
m."user_id",
m."type",
date_trunc('day', m."measured_at") AS bucket_start
FROM measurements m
WHERE m."deleted_at" IS NULL
) mt
LEFT JOIN measurement_rollups r
ON r."user_id" = mt."user_id"
AND r."type" = mt."type"
AND r."granularity" = 'DAY'
AND r."bucket_start" = mt."bucket_start"
WHERE r."bucket_start" IS NULL
`;
if (users.length === 0) {