From 44bfb6701745876c3057ab77523fded8b9ed94a7 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Wed, 20 May 2026 11:42:56 +0100 Subject: [PATCH] perf(webapp): speed up EnvironmentVariableValue lookups The /api/v1/projects/:projectRef/envvars/:slug/:name route calls `getEnvironmentWithRedactedSecrets`, which runs a Prisma findMany on EnvironmentVariableValue filtered by environmentId + isSecret. Two issues compound: 1. The only existing indexes are the primary key and a unique on (variableId, environmentId), so environmentId is never the leading column. Postgres falls back to a Parallel Seq Scan over the whole table for what is, in practice, a handful of rows per environment. 2. The repository was running this read on the writer client. Two changes: - Add a btree index on EnvironmentVariableValue(environmentId) so the planner switches to an index scan and the CPU + buffer-pin (LWLock:BufferContent) cost from concurrent calls disappears. - Route this one findMany through the read replica via a new `replicaClient` constructor param on the repository (defaulting to $replica, mirroring how prismaClient defaults to prisma). Writes and read-after-write methods stay on the primary. The composite (variableId, environmentId) unique stays in place; the new index is purely additive. --- .server-changes/env-var-value-environment-id-index.md | 6 ++++++ .../environmentVariablesRepository.server.ts | 9 ++++++--- .../migration.sql | 3 +++ internal-packages/database/prisma/schema.prisma | 1 + 4 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 .server-changes/env-var-value-environment-id-index.md create mode 100644 internal-packages/database/prisma/migrations/20260520120000_add_environment_variable_value_environment_id_idx/migration.sql diff --git a/.server-changes/env-var-value-environment-id-index.md b/.server-changes/env-var-value-environment-id-index.md new file mode 100644 index 00000000000..1c063859237 --- /dev/null +++ b/.server-changes/env-var-value-environment-id-index.md @@ -0,0 +1,6 @@ +--- +area: webapp +type: improvement +--- + +Speed up env-var lookups on the projects API by indexing `EnvironmentVariableValue.environmentId`. diff --git a/apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts b/apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts index f2ca46d4d3a..9cc41995664 100644 --- a/apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts +++ b/apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts @@ -2,7 +2,7 @@ import { Prisma, type PrismaClient, type RuntimeEnvironmentType } from "@trigger import type { AuthenticatedEnvironment } from "@trigger.dev/core/v3/auth/environment"; import { z } from "zod"; import { environmentFullTitle } from "~/components/environments/EnvironmentLabel"; -import { $transaction, prisma } from "~/db.server"; +import { $replica, $transaction, prisma, type PrismaReplicaClient } from "~/db.server"; import { env } from "~/env.server"; import { getSecretStore } from "~/services/secrets/secretStore.server"; import { generateFriendlyId } from "../friendlyIdentifiers"; @@ -47,7 +47,10 @@ function parseSecretKey(key: string) { const SecretValue = z.object({ secret: z.string() }); export class EnvironmentVariablesRepository implements Repository { - constructor(private prismaClient: PrismaClient = prisma) {} + constructor( + private prismaClient: PrismaClient = prisma, + private replicaClient: PrismaReplicaClient = $replica + ) {} async create(projectId: string, options: CreateEnvironmentVariables): Promise { const project = await this.prismaClient.project.findFirst({ @@ -582,7 +585,7 @@ export class EnvironmentVariablesRepository implements Repository { const variables = await this.getEnvironment(projectId, environmentId, parentEnvironmentId); // Get the keys of all secret variables - const secretValues = await this.prismaClient.environmentVariableValue.findMany({ + const secretValues = await this.replicaClient.environmentVariableValue.findMany({ where: { environmentId: parentEnvironmentId ? { in: [environmentId, parentEnvironmentId] } diff --git a/internal-packages/database/prisma/migrations/20260520120000_add_environment_variable_value_environment_id_idx/migration.sql b/internal-packages/database/prisma/migrations/20260520120000_add_environment_variable_value_environment_id_idx/migration.sql new file mode 100644 index 00000000000..9aaf4d930c6 --- /dev/null +++ b/internal-packages/database/prisma/migrations/20260520120000_add_environment_variable_value_environment_id_idx/migration.sql @@ -0,0 +1,3 @@ +-- CreateIndex +CREATE INDEX CONCURRENTLY IF NOT EXISTS "EnvironmentVariableValue_environmentId_idx" + ON "EnvironmentVariableValue"("environmentId"); diff --git a/internal-packages/database/prisma/schema.prisma b/internal-packages/database/prisma/schema.prisma index 7e32a96d805..ae7cb34bf5b 100644 --- a/internal-packages/database/prisma/schema.prisma +++ b/internal-packages/database/prisma/schema.prisma @@ -2020,6 +2020,7 @@ model EnvironmentVariableValue { lastUpdatedBy Json? @@unique([variableId, environmentId]) + @@index([environmentId]) } model Checkpoint {