perf(webapp): index EnvironmentVariableValue.environmentId (#3675)
Env-var lookups via `GET /api/v1/projects/:projectRef/envvars/:slug/:name` run a Prisma `findMany` on `EnvironmentVariableValue` filtered by `environmentId` + `isSecret`. The only existing indexes are the primary key and a unique on `(variableId, environmentId)`, so `environmentId` is never the leading column — the planner falls back to a Parallel Seq Scan over the whole table to find what is, in practice, a handful of rows per environment. Two changes: - Add a btree index on `EnvironmentVariableValue(environmentId)` so the planner switches to an index scan. The composite `(variableId, environmentId)` unique stays in place; the new index is purely additive. - Route the `findMany` inside `getEnvironmentWithRedactedSecrets` 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. ## Test plan - [ ] `pnpm run typecheck --filter webapp` - [ ] Confirm `EXPLAIN` plan flips from Parallel Seq Scan to an index scan - [ ] Existing env-var route tests still pass
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
area: webapp
|
||||
type: improvement
|
||||
---
|
||||
|
||||
Speed up env-var lookups on the projects API by indexing `EnvironmentVariableValue.environmentId`.
|
||||
@@ -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<CreateResult> {
|
||||
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] }
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
-- CreateIndex
|
||||
CREATE INDEX CONCURRENTLY IF NOT EXISTS "EnvironmentVariableValue_environmentId_idx"
|
||||
ON "EnvironmentVariableValue"("environmentId");
|
||||
@@ -2020,6 +2020,7 @@ model EnvironmentVariableValue {
|
||||
lastUpdatedBy Json?
|
||||
|
||||
@@unique([variableId, environmentId])
|
||||
@@index([environmentId])
|
||||
}
|
||||
|
||||
model Checkpoint {
|
||||
|
||||
Reference in New Issue
Block a user