Files
HealthLog/src/lib/jobs/idempotency-cleanup.ts
T
Marc-André BombeckandClaude Opus 4.7 b76d579717 feat(jobs): daily cleanup job for expired idempotency_keys (#120)
`withIdempotency()` only purges rows lazily on the next lookup with
the same `(userId, key, method, path)` tuple — most rows never see a
retry, so without a sweeper the table grows unbounded with stale
24h-expired entries (Security-Review HIGH-2 from PR #117).

Adds a pg-boss queue that runs daily at 03:00 Europe/Berlin (parallel
to the weekly Sunday data-backup job) and bulk-deletes expired rows.
Outcome surfaces as a Wide Event annotation
`idempotency_cleanup_deleted` so the run is queryable in Loki.

Helper extracted to `src/lib/jobs/idempotency-cleanup.ts` for clean
unit testing without mocking pg-boss internals.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 20:30:30 +02:00

19 lines
624 B
TypeScript

/**
* Daily cleanup for the `idempotency_keys` table.
*
* `withIdempotency()` (src/lib/idempotency.ts) only purges a row lazily
* on the next lookup with the same `(userId, key, method, path)` tuple
* — most rows never see another retry, so without a sweeper the table
* grows unbounded with stale 24h-expired entries.
*/
import type { PrismaClient } from "@/generated/prisma/client";
export async function cleanupExpiredIdempotencyKeys(
prisma: PrismaClient,
): Promise<number> {
const { count } = await prisma.idempotencyKey.deleteMany({
where: { expiresAt: { lt: new Date() } },
});
return count;
}