From ce368dd8e0a519c85b3ed398ff235e10f53e4a47 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Mon, 10 Aug 2026 13:54:18 +0100 Subject: [PATCH] perf(database): index EnvironmentVariableValue.valueReferenceId so secret deletes stop seq-scanning (#4555) ## Why this change `EnvironmentVariableValue.valueReference` is an `onDelete: SetNull` foreign key. Deleting a `SecretReference` (the env var edit/delete path for secret values) fires the cascade `UPDATE ONLY "EnvironmentVariableValue" SET "valueReferenceId" = NULL WHERE $1 = "valueReferenceId"`. That cascade is scan-shaped: with no index on `valueReferenceId`, it reads the entire table to find the rows referencing the deleted secret. The parent `SecretReference` delete does almost no work itself; its latency is dominated by this cascade. ## Diagnosis `EnvironmentVariableValue` was indexed on `environmentId` and `(variableId, environmentId)`, but not on `valueReferenceId`. The SET NULL cascade therefore did a full sequential scan of the whole table. Two sibling SET NULL cascades on the same delete (`OrganizationIntegration.tokenReferenceId`, `User.mfaSecretReferenceId`) are index-backed and stay fast, which isolates the missing index as the cause. ## Change Add `@@index([valueReferenceId])` on `EnvironmentVariableValue`, created with `CREATE INDEX CONCURRENTLY IF NOT EXISTS` so `prisma migrate deploy` stays safe on a live table. ## Benchmark (local, seeded) Local Postgres seeded with 1,000,000 `EnvironmentVariableValue` rows, `EXPLAIN (ANALYZE, BUFFERS)` on the SET NULL cascade with zero matching rows (the worst case: reads the whole table, affects nothing): | | before | after | |---|---|---| | plan | Seq Scan (1M rows) | Bitmap Index Scan | | execution | 183 ms | 2.8 ms | In a variant where the secret matched several thousand rows, the parent `SecretReference` delete's `EnvironmentVariableValue_valueReferenceId_fkey` trigger dropped from 216 ms to 88 ms (the residual is the heap work of nulling those rows). ## Expected impact The cascade drops from a full-table sequential scan to a targeted index lookup. The win grows with the table, so the benefit is larger than the seeded numbers above. ## Risks - One extra btree to maintain on `EnvironmentVariableValue` writes; small, single-column, and it should be pre-created before the migration deploys (per the repo index rules). - No behavior change: same rows nulled, no ordering or result-set change, read paths untouched. Companion to the same fix on `ProjectAlert.channelId`. --- .../environment-variable-value-reference-id-index.md | 6 ++++++ .../migration.sql | 2 ++ internal-packages/database/prisma/schema.prisma | 1 + 3 files changed, 9 insertions(+) create mode 100644 .server-changes/environment-variable-value-reference-id-index.md create mode 100644 internal-packages/database/prisma/migrations/20260810130000_add_environment_variable_value_reference_id_index/migration.sql diff --git a/.server-changes/environment-variable-value-reference-id-index.md b/.server-changes/environment-variable-value-reference-id-index.md new file mode 100644 index 000000000..32fee03f1 --- /dev/null +++ b/.server-changes/environment-variable-value-reference-id-index.md @@ -0,0 +1,6 @@ +--- +area: webapp +type: improvement +--- + +Deleting or editing a secret environment variable is now fast and no longer slows down as a project accumulates variables. diff --git a/internal-packages/database/prisma/migrations/20260810130000_add_environment_variable_value_reference_id_index/migration.sql b/internal-packages/database/prisma/migrations/20260810130000_add_environment_variable_value_reference_id_index/migration.sql new file mode 100644 index 000000000..c69882cbb --- /dev/null +++ b/internal-packages/database/prisma/migrations/20260810130000_add_environment_variable_value_reference_id_index/migration.sql @@ -0,0 +1,2 @@ +-- CreateIndex +CREATE INDEX CONCURRENTLY IF NOT EXISTS "EnvironmentVariableValue_valueReferenceId_idx" ON "public"."EnvironmentVariableValue"("valueReferenceId"); diff --git a/internal-packages/database/prisma/schema.prisma b/internal-packages/database/prisma/schema.prisma index 5ce51d0db..3cf141fe0 100644 --- a/internal-packages/database/prisma/schema.prisma +++ b/internal-packages/database/prisma/schema.prisma @@ -2067,6 +2067,7 @@ model EnvironmentVariableValue { @@unique([variableId, environmentId]) @@index([environmentId]) + @@index([valueReferenceId]) } model Checkpoint {