mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 05:12:14 +00:00
fix(rollups): defer the dense fold when the backfill check cannot read the queue
The check whether an account's full rollup backfill is queued or running answered false on any error, so a failed read let the dense fold delete raw samples the backfill still had to read. A missing queue table still answers false, since a web-only deployment has no backfill; any other failure now counts as pending and is annotated. A backfill that failed for good is still not pending, as the comment now says.
This commit is contained in:
1 parent
ec0f67f850
commit
6b820fa3c5
2 files changed
+44
-4
No files matched your search
@@ -24,6 +24,7 @@
|
||||
import { activeAccountSql } from "@/lib/account-deletion/active-account";
|
||||
import { prisma } from "@/lib/db";
|
||||
import { getGlobalBoss } from "@/lib/jobs/boss-instance";
|
||||
import { annotate } from "@/lib/logging/context";
|
||||
import {
|
||||
applyStatementBudget,
|
||||
recomputeUserRollups,
|
||||
@@ -199,13 +200,24 @@ export async function sendRollupFullBackfillContinuation(
|
||||
* leaves such an account alone (`ConsolidationOptions.deferUser`): it deletes
|
||||
* the raw samples a fold still has to read, and a year the full backfill
|
||||
* reached after the dense fold would take its day figures from hourly means.
|
||||
* False when the queue schema cannot be read, as a web-only deployment has
|
||||
* no full backfill either.
|
||||
*
|
||||
* False when there is no queue table (a web-only deployment has no full
|
||||
* backfill either). Any other failure to read the queue counts as pending:
|
||||
* the dense fold's deletion cannot be undone, and deferring an account costs
|
||||
* one night. The failure is annotated on the caller's event.
|
||||
*
|
||||
* A full fold that failed for good (retries spent) is not pending, and the
|
||||
* dense fold goes ahead on that account; the system job alert reports the
|
||||
* failed backfill, and the next boot's discovery enqueues it again.
|
||||
*/
|
||||
export async function isRollupFullBackfillPending(
|
||||
userId: string,
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const [queue] = await prisma.$queryRaw<Array<{ present: boolean }>>`
|
||||
SELECT to_regclass('pgboss.job') IS NOT NULL AS present
|
||||
`;
|
||||
if (!queue?.present) return false;
|
||||
const rows = await prisma.$queryRaw<Array<{ n: number }>>`
|
||||
SELECT count(*)::int AS n
|
||||
FROM pgboss.job
|
||||
@@ -214,8 +226,15 @@ export async function isRollupFullBackfillPending(
|
||||
AND data->>'userId' = ${userId}
|
||||
`;
|
||||
return (rows[0]?.n ?? 0) > 0;
|
||||
} catch {
|
||||
return false;
|
||||
} catch (err) {
|
||||
annotate({
|
||||
meta: {
|
||||
rollup_backfill_pending_check_failed: true,
|
||||
rollup_backfill_pending_check_error:
|
||||
err instanceof Error ? err.message.slice(0, 200) : String(err),
|
||||
},
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -344,4 +344,25 @@ describe("isRollupFullBackfillPending", () => {
|
||||
await boss.complete(ROLLUP_FULL_BACKFILL_QUEUE, job.id);
|
||||
expect(await isRollupFullBackfillPending("user-a")).toBe(false);
|
||||
});
|
||||
|
||||
it("counts a queue it cannot read as pending, so the dense fold waits", async () => {
|
||||
// The table is there but the read fails.
|
||||
await prisma.$executeRawUnsafe(
|
||||
`ALTER TABLE pgboss.job RENAME COLUMN data TO data_unreadable`,
|
||||
);
|
||||
try {
|
||||
expect(await isRollupFullBackfillPending("user-a")).toBe(true);
|
||||
} finally {
|
||||
await prisma.$executeRawUnsafe(
|
||||
`ALTER TABLE pgboss.job RENAME COLUMN data_unreadable TO data`,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("isRollupFullBackfillPending without a queue", () => {
|
||||
it("is false where there is no queue table at all", async () => {
|
||||
await prisma.$executeRawUnsafe(`DROP SCHEMA IF EXISTS pgboss CASCADE`);
|
||||
expect(await isRollupFullBackfillPending("user-a")).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user