From b3d303217ce639facd40a6a7d15a8c8d08406d92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 19 Jun 2026 20:52:31 +0200 Subject: [PATCH] perf(db): index session and challenge expiry columns The sliding-30-day session reaper and the passkey-challenge sweep both filter on expires_at, but neither sessions nor auth_challenges indexed that column, so the cleanup degraded to a sequential scan on a busy instance. Every sibling expiry table already indexes its expiry column; add the two missing indexes via migration 0178. --- .../migration.sql | 19 +++++++++++++++++++ prisma/schema.prisma | 6 ++++++ 2 files changed, 25 insertions(+) create mode 100644 prisma/migrations/0178_v1187_session_expiry_indexes/migration.sql diff --git a/prisma/migrations/0178_v1187_session_expiry_indexes/migration.sql b/prisma/migrations/0178_v1187_session_expiry_indexes/migration.sql new file mode 100644 index 000000000..36ec5317d --- /dev/null +++ b/prisma/migrations/0178_v1187_session_expiry_indexes/migration.sql @@ -0,0 +1,19 @@ +-- v1.18.7 — expiry indexes for the two tables swept by the session reaper. +-- +-- `sessions` (sliding-30-day session expiry) and `auth_challenges` (passkey +-- registration / authentication challenges) are both pruned on `expires_at`, +-- but neither carried an index on that column. On a busy multi-user instance +-- the cleanup degraded to a sequential scan. Every sibling expiry table +-- (`refresh_tokens`, `whoop_oauth_states`, `idempotency_keys`, +-- `withings_oauth_states`, …) already indexes its expiry column; this brings +-- the two stragglers in line. Additive; no data touched. +-- +-- Reversibility (down): +-- DROP INDEX IF EXISTS "sessions_expires_at_idx"; +-- DROP INDEX IF EXISTS "auth_challenges_expires_at_idx"; + +-- CreateIndex +CREATE INDEX IF NOT EXISTS "sessions_expires_at_idx" ON "sessions"("expires_at"); + +-- CreateIndex +CREATE INDEX IF NOT EXISTS "auth_challenges_expires_at_idx" ON "auth_challenges"("expires_at"); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c2f620a3c..708de1110 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -721,6 +721,9 @@ model Session { user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) + // v1.18.7 — the sliding-30-day session reaper sweeps on `expiresAt`; without + // this index the cleanup degrades to a sequential scan on a busy instance. + @@index([expiresAt]) @@map("sessions") } @@ -735,6 +738,9 @@ model AuthChallenge { user User? @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) + // v1.18.7 — challenges are swept by expiry; index the column the reaper + // filters on so the cleanup stays index-backed. + @@index([expiresAt]) @@map("auth_challenges") }