From 4658cd0721518944edd4bf4641ab831baa3a0674 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Wed, 12 Aug 2026 14:03:39 +0100 Subject: [PATCH] perf(database): index WorkerDeployment on (environmentId, status, id) for the deployments list (#4591) ## What Adds a composite index `@@index([environmentId, status, id])` to `WorkerDeployment`. The public deployments list (`GET /api/v1/deployments`) filters by `status` and paginates by `id` descending. The existing indexes cover `(environmentId, createdAt)` and the PK, but nothing covers `status`. So for a status filter Postgres walks back through the environment's deployments discarding non-matching statuses, reading roughly 350 rows for every 1 returned (p99 ~1.1s on the busiest environments). The new index makes the status filter index-satisfied and lets `id` serve both the cursor range and the `ORDER BY id DESC`, bounding the read to a single page. Full composite (not partial) because callers filter by arbitrary status values with no single dominant one. ## Query ```sql SELECT ... FROM "WorkerDeployment" WHERE "environmentId" = $1 AND "status" = $2 [AND "id" < $3] ORDER BY "id" DESC LIMIT $4; ``` Source: `apps/webapp/app/routes/api.v1.deployments.ts`. ## Evidence Reproduced on an isolated stack: one environment seeded with 7,000 deployments, the filtered status appearing 1 in 333 rows. Before (no index): ``` Seq Scan on "WorkerDeployment" (rows=21) Rows Removed by Filter: 6979 Buffers: shared hit=206 Execution Time: 2.9 ms (+ a sort for id desc) ``` After (with the index): ``` Index Scan Backward using "WorkerDeployment_environmentId_status_id_idx" Index Cond: (environmentId = $1 AND status = $2) Buffers: shared hit=23 Execution Time: 0.43 ms ``` Rows-removed-by-filter drops to 0; buffers 206 -> 23. The cursor (mid-pagination) variant uses the same index with all three predicates as the index condition. A dense/common status keeps the cheap PK backward scan (already fine); the index targets exactly the rare-status paths that were amplified. End-to-end against the running webapp API: `?status=FAILED` returns the correct newest-first page and paginates correctly across pages, and the emitted SQL matches the query above. ## Rollout - Index only, `CREATE INDEX CONCURRENTLY IF NOT EXISTS` in its own migration file. Online-safe under write load. - Pre-apply the index in production before the migration deploys, per repo convention (the migration is then a no-op). - Rollback: drop the index. No data migration. refs TRI-13171 --- .server-changes/worker-deployment-list-status-index.md | 6 ++++++ .../migration.sql | 1 + internal-packages/database/prisma/schema.prisma | 1 + 3 files changed, 8 insertions(+) create mode 100644 .server-changes/worker-deployment-list-status-index.md create mode 100644 internal-packages/database/prisma/migrations/20260812130000_add_worker_deployment_environment_id_status_id_index/migration.sql diff --git a/.server-changes/worker-deployment-list-status-index.md b/.server-changes/worker-deployment-list-status-index.md new file mode 100644 index 000000000..e3fa07977 --- /dev/null +++ b/.server-changes/worker-deployment-list-status-index.md @@ -0,0 +1,6 @@ +--- +area: webapp +type: improvement +--- + +Loading the deployments list is now faster, especially when filtering by deployment status on projects with many deployments. diff --git a/internal-packages/database/prisma/migrations/20260812130000_add_worker_deployment_environment_id_status_id_index/migration.sql b/internal-packages/database/prisma/migrations/20260812130000_add_worker_deployment_environment_id_status_id_index/migration.sql new file mode 100644 index 000000000..752528dd2 --- /dev/null +++ b/internal-packages/database/prisma/migrations/20260812130000_add_worker_deployment_environment_id_status_id_index/migration.sql @@ -0,0 +1 @@ +CREATE INDEX CONCURRENTLY IF NOT EXISTS "WorkerDeployment_environmentId_status_id_idx" ON "public"."WorkerDeployment"("environmentId", "status", "id"); diff --git a/internal-packages/database/prisma/schema.prisma b/internal-packages/database/prisma/schema.prisma index 868e02eb2..f705e4006 100644 --- a/internal-packages/database/prisma/schema.prisma +++ b/internal-packages/database/prisma/schema.prisma @@ -2214,6 +2214,7 @@ model WorkerDeployment { @@unique([environmentId, version]) @@index([commitSHA]) @@index([environmentId, createdAt]) + @@index([environmentId, status, id]) } enum WorkerDeploymentStatus {