fix(measurements): hold the dense fold back while an account's rollups rebuild

The full rollup backfill refills each day's figures from the raw
readings, and the dense fold deletes those readings once it has their
hourly means. On a large account the backfill can run longer than the
ten minutes the fold's boot job waits, and a year it reached after the
fold had taken it would compute its days from hourly means: the day
count, minimum and maximum of every folded day would change.

The consolidation walk now asks a per-account deferUser hook before it
reads an account, and both entries of the dense fold (the per-account
boot job and the nightly drain) pass isRollupFullBackfillPending, which
looks for a queued or running full fold of the account in pg-boss. The
account is folded on a later run, once its rollup tier is rebuilt.
This commit is contained in:
Marc-André Bombeck committed 2026-10-29 09:28:31 +01:00
1 parent 9eed407ccc
commit 07cf9bba3e
7 files changed
+100 -14

No files matched your search

+42 -1
View File
@@ -11,13 +11,24 @@
* reading in the window, finds the same accounts as the statement it
* replaced, kept verbatim below as the reference.
*/
import { beforeEach, describe, expect, it, vi } from "vitest";
import { PgBoss } from "pg-boss";
import {
afterAll,
beforeAll,
beforeEach,
describe,
expect,
it,
vi,
} from "vitest";
import { Prisma, type MeasurementSource } from "@/generated/prisma/client";
import { activeAccountSql } from "@/lib/account-deletion/active-account";
import {
enqueueBootTimeRollupBackfill,
foldUserRollupHistory,
isRollupFullBackfillPending,
ROLLUP_FULL_BACKFILL_QUEUE,
ROLLUP_FULL_BACKFILL_STATEMENT_BUDGET_MS,
} from "@/lib/rollups/full-backfill";
import {
@@ -304,3 +315,33 @@ describe("enqueueBootTimeRollupBackfill", () => {
expect(found).toEqual(await referenceDiscovery());
});
});
describe("isRollupFullBackfillPending", () => {
let boss: PgBoss;
beforeAll(async () => {
boss = new PgBoss({
connectionString: process.env.DATABASE_URL,
supervise: false,
schedule: false,
});
await boss.start();
await boss.createQueue(ROLLUP_FULL_BACKFILL_QUEUE);
}, 60_000);
afterAll(async () => {
await boss?.stop({ graceful: false, close: true });
await prisma.$executeRawUnsafe(`DROP SCHEMA IF EXISTS pgboss CASCADE`);
});
it("is true while the account's full fold is queued or running, and only for that account", async () => {
expect(await isRollupFullBackfillPending("user-a")).toBe(false);
await boss.send(ROLLUP_FULL_BACKFILL_QUEUE, { userId: "user-a" });
expect(await isRollupFullBackfillPending("user-a")).toBe(true);
expect(await isRollupFullBackfillPending("user-b")).toBe(false);
const [job] = await boss.fetch(ROLLUP_FULL_BACKFILL_QUEUE);
expect(await isRollupFullBackfillPending("user-a")).toBe(true);
await boss.complete(ROLLUP_FULL_BACKFILL_QUEUE, job.id);
expect(await isRollupFullBackfillPending("user-a")).toBe(false);
});
});