Skip to content

Commit 8d0f693

Browse files
authored
perf(webapp): drop archived branch environments from project env loads (#4595)
## What Several project pages loaded **every** `RuntimeEnvironment` row for a project, including the archived preview-branch environments that are never shown in the UI. On a project with heavy preview-branch usage that means thousands of rows per load, producing a large result set and a rare multi-second tail on the environment lookup (~30s outlier observed via Insights on `RuntimeEnvironment` projectId lookups, fingerprint `f2b3ecab…`). The tail is dominated by the size of the result being parsed/transferred, not by the query plan (it already used `RuntimeEnvironment_projectId_idx` with no over-read). So the fix is to stop returning archived branch environments. ## Diagnosis correction The ticket framed this as a "large `projectId IN` list" and suggested bounding the IN list / cursor pagination. It's actually a Prisma **nested relation load** on a *single-project* `project.findFirst`, so the `IN (...)` holds one projectId and the trailing `OFFSET $1` is Prisma's relation-subquery artifact. The 4,644 rows in the observed execution were **one project with ~4,644 environments** (accumulated archived branches), not many projects. ## Change Filter the `environments` relation load to `archivedAt: null` (base envs never archive, so only archived preview branches are excluded): - `ProjectPresenter.server.ts` - `orgs.$organizationSlug.projects.$projectParam.{concurrency,apikeys,environment-variables,settings}.ts` (best-env resolvers) And remove an **unused** `environments` select from `DeploymentListPresenter.server.ts` (it was selected but never read). `loadProjectEnvironments` (replay route) already filters `archivedAt: null` + env type; this change follows that existing precedent. ## Evidence (isolated stack, seeded one project with 2,000 archived branch envs + 4 active) `EXPLAIN (ANALYZE)` of the exact presenter sub-select: | | rows returned | index | |---|---|---| | before (unfiltered) | **2004** | `RuntimeEnvironment_projectId_idx` | | after (`archivedAt IS NULL`) | **4** (`Rows Removed by Filter: 2000`) | same index, no plan change | 500x fewer rows to the client, which is what removes the parse-on-load tail. No new index needed. `typecheck --filter webapp` clean. UI verified: project layout, Deploys page, and the concurrency best-env redirect all render with the 2,000 archived branches present in the DB and zero console errors. ## Rollout / rollback Straight deploy, no migration. Rollback is revert-only (read-path filter, no data change). Old and in-flight rows read correctly under both the old and new code. ## Limitation A project with thousands of *active* branches would still load them all; in practice active branches are few (branches are archived when their work merges). Hard-bounding active branches would be a larger change and is out of scope here.
1 parent bc3a33b commit 8d0f693

9 files changed

Lines changed: 14 additions & 18 deletions
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: improvement
4+
---
5+
6+
Project pages now load faster for projects with a large number of preview branches, by no longer loading archived branch environments that aren't shown.

apps/webapp/app/presenters/ProjectPresenter.server.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export class ProjectPresenter {
3030
version: true,
3131
externalRef: true,
3232
environments: {
33+
where: { archivedAt: null },
3334
select: {
3435
id: true,
3536
slug: true,

apps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export class SelectBestEnvironmentPresenter {
4646
include: {
4747
organization: true,
4848
environments: {
49+
where: { archivedAt: null },
4950
select: {
5051
id: true,
5152
type: true,
@@ -71,6 +72,7 @@ export class SelectBestEnvironmentPresenter {
7172
include: {
7273
organization: true,
7374
environments: {
75+
where: { archivedAt: null },
7476
select: {
7577
id: true,
7678
type: true,

apps/webapp/app/presenters/v3/DeploymentListPresenter.server.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -43,24 +43,6 @@ export class DeploymentListPresenter {
4343
const project = await this.#prismaClient.project.findFirstOrThrow({
4444
select: {
4545
id: true,
46-
environments: {
47-
select: {
48-
id: true,
49-
type: true,
50-
slug: true,
51-
orgMember: {
52-
select: {
53-
user: {
54-
select: {
55-
id: true,
56-
name: true,
57-
displayName: true,
58-
},
59-
},
60-
},
61-
},
62-
},
63-
},
6446
connectedGithubRepository: {
6547
select: {
6648
branchTracking: true,

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam._index/route.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
1717
},
1818
include: {
1919
environments: {
20+
where: { archivedAt: null },
2021
select: {
2122
id: true,
2223
type: true,

apps/webapp/app/routes/orgs.$organizationSlug.projects.$projectParam.apikeys.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
1616
},
1717
include: {
1818
environments: {
19+
where: { archivedAt: null },
1920
select: {
2021
id: true,
2122
type: true,

apps/webapp/app/routes/orgs.$organizationSlug.projects.$projectParam.concurrency.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
1616
},
1717
include: {
1818
environments: {
19+
where: { archivedAt: null },
1920
select: {
2021
id: true,
2122
type: true,

apps/webapp/app/routes/orgs.$organizationSlug.projects.$projectParam.environment-variables.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
1616
},
1717
include: {
1818
environments: {
19+
where: { archivedAt: null },
1920
select: {
2021
id: true,
2122
type: true,

apps/webapp/app/routes/orgs.$organizationSlug.projects.$projectParam.settings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
1616
},
1717
include: {
1818
environments: {
19+
where: { archivedAt: null },
1920
select: {
2021
id: true,
2122
type: true,

0 commit comments

Comments
 (0)