mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 22:02:16 +00:00
perf(reminders): bound the Vorsorge cron scan to due reminders
The measurement-reminder tick loaded every enabled reminder across all tenants every 15 minutes and gated due-ness in Node, so the shipped measurement_reminders_user_id_next_due_at_idx went unused. Add a nextDueAt bound (not null, lte now plus one tick of slack) to the query so Postgres scans only rows that could plausibly fire. nextDueAt is stamped at the notify-hour boundary, so anything due is already past; non-recurring reminders carry a null nextDueAt and can never fire, which the in-Node predicate already short-circuits. A reminder satisfied early re-anchors once it crosses due, before any nudge, so dropping future rows is safe. The in-Node hour gate stays the authoritative fire decision.
This commit is contained in:
1 parent
b87ded090c
commit
33e7f20736
2 files changed
+50
-1
No files matched your search
@@ -268,6 +268,33 @@ describe("runMeasurementReminderTick", () => {
|
||||
expect(updates[0].data.nextDueAt).toBeInstanceOf(Date);
|
||||
});
|
||||
|
||||
it("bounds the scan with a nextDueAt filter so the index is used", async () => {
|
||||
const { prisma } = makePrisma({
|
||||
reminders: [reminder({})],
|
||||
measurementMatch: null,
|
||||
});
|
||||
const dispatch = vi.fn<DispatchFn>(async () => OK);
|
||||
|
||||
await runMeasurementReminderTick(prisma as never, NINE_LOCAL, { dispatch });
|
||||
|
||||
const calls = prisma.measurementReminder.findMany.mock
|
||||
.calls as unknown as unknown[][];
|
||||
const args = calls[0][0] as {
|
||||
where: {
|
||||
deletedAt: null;
|
||||
enabled: boolean;
|
||||
nextDueAt: { not: null; lte: Date };
|
||||
};
|
||||
};
|
||||
expect(args.where.enabled).toBe(true);
|
||||
expect(args.where.deletedAt).toBeNull();
|
||||
expect(args.where.nextDueAt.not).toBeNull();
|
||||
// Floor is now + one tick of slack; anything due (<= floor) is included.
|
||||
expect(args.where.nextDueAt.lte.getTime()).toBe(
|
||||
NINE_LOCAL.getTime() + 15 * 60_000,
|
||||
);
|
||||
});
|
||||
|
||||
it("skips a reminder outside its notify-hour window", async () => {
|
||||
const { prisma } = makePrisma({
|
||||
reminders: [reminder({ measurementType: null })],
|
||||
|
||||
@@ -41,6 +41,14 @@ import {
|
||||
type ReminderScheduleInput,
|
||||
} from "@/lib/measurement-reminders/scheduling";
|
||||
|
||||
/**
|
||||
* Slack added to `now` when scanning for due reminders, so a `nextDueAt`
|
||||
* stamped just ahead of a tick still falls inside the same hour window.
|
||||
* One tick interval (15 min) — small enough that the in-Node hour gate stays
|
||||
* the authoritative fire decision.
|
||||
*/
|
||||
const DUE_QUERY_SLACK_MS = 15 * 60_000;
|
||||
|
||||
export interface MeasurementReminderSummary {
|
||||
candidatesScanned: number;
|
||||
inWindow: number;
|
||||
@@ -141,8 +149,22 @@ export async function runMeasurementReminderTick(
|
||||
failed: 0,
|
||||
};
|
||||
|
||||
// Bound the scan to reminders that could plausibly fire this tick so Postgres
|
||||
// uses `measurement_reminders_user_id_next_due_at_idx` instead of loading the
|
||||
// whole cross-tenant enabled set 4×/hour. `nextDueAt` is stamped at the
|
||||
// notify-hour boundary, so anything due is already <= now; the small slack
|
||||
// (one tick interval) covers a stamp landing just ahead of a :00 tick. A
|
||||
// null `nextDueAt` is a non-recurring reminder that can never fire — the
|
||||
// in-Node `evaluateMeasurementReminderDue` short-circuits it anyway, so
|
||||
// excluding it here is parity. Reminders satisfied early re-anchor once they
|
||||
// cross due, before any nudge fires, so dropping future rows is safe.
|
||||
const dueFloor = new Date(now.getTime() + DUE_QUERY_SLACK_MS);
|
||||
const reminders = await prisma.measurementReminder.findMany({
|
||||
where: { deletedAt: null, enabled: true },
|
||||
where: {
|
||||
deletedAt: null,
|
||||
enabled: true,
|
||||
nextDueAt: { not: null, lte: dueFloor },
|
||||
},
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
|
||||
Reference in new issue
Block a user