Skip to content

Commit 34db26d

Browse files
committed
Fix for Schedules list page slow loading
Getting BackgroundWorkerTask was very slow (Prisma was getting every single one…)
1 parent f579afb commit 34db26d

File tree

6 files changed

+50
-33
lines changed

6 files changed

+50
-33
lines changed

apps/webapp/app/components/runs/v3/ScheduleFilters.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ export const ScheduleListFilters = z.object({
2424
.string()
2525
.optional()
2626
.transform((value) => (value ? value.split(",") : undefined)),
27-
environments: z
28-
.string()
29-
.optional()
30-
.transform((value) => (value ? value.split(",") : undefined)),
3127
type: z.union([z.literal("declarative"), z.literal("imperative")]).optional(),
3228
search: z.string().optional(),
3329
});
@@ -44,7 +40,7 @@ export function ScheduleFilters({ possibleTasks }: ScheduleFiltersProps) {
4440
const navigate = useNavigate();
4541
const location = useOptimisticLocation();
4642
const searchParams = new URLSearchParams(location.search);
47-
const { environments, tasks, page, search, type } = ScheduleListFilters.parse(
43+
const { tasks, page, search, type } = ScheduleListFilters.parse(
4844
Object.fromEntries(searchParams.entries())
4945
);
5046

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

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ import { getLimit } from "~/services/platform.v3.server";
66
import { CheckScheduleService } from "~/v3/services/checkSchedule.server";
77
import { calculateNextScheduledTimestamp } from "~/v3/utils/calculateNextSchedule.server";
88
import { BasePresenter } from "./basePresenter.server";
9+
import { findCurrentWorkerFromEnvironment } from "~/v3/models/workerDeployment.server";
10+
import { ServiceValidationError } from "~/v3/services/baseService.server";
911

1012
type ScheduleListOptions = {
1113
projectId: string;
14+
environmentId: string;
1215
userId?: string;
1316
pageSize?: number;
1417
} & ScheduleListFilters;
@@ -42,8 +45,8 @@ export class ScheduleListPresenter extends BasePresenter {
4245
public async call({
4346
userId,
4447
projectId,
48+
environmentId,
4549
tasks,
46-
environments,
4750
search,
4851
page,
4952
type,
@@ -84,16 +87,45 @@ export class ScheduleListPresenter extends BasePresenter {
8487
},
8588
});
8689

90+
const environment = project.environments.find((env) => env.id === environmentId);
91+
if (!environment) {
92+
throw new ServiceValidationError("No matching environment for project", 404);
93+
}
94+
8795
const schedulesCount = await CheckScheduleService.getUsedSchedulesCount({
8896
prisma: this._replica,
8997
environments: project.environments,
9098
});
9199

100+
const limit = await getLimit(project.organizationId, "schedules", 100_000_000);
101+
102+
//get the latest BackgroundWorker
103+
const latestWorker = await findCurrentWorkerFromEnvironment(environment, this._replica);
104+
if (!latestWorker) {
105+
return {
106+
currentPage: 1,
107+
totalPages: 1,
108+
totalCount: 0,
109+
schedules: [],
110+
possibleTasks: [],
111+
hasFilters,
112+
limits: {
113+
used: schedulesCount,
114+
limit,
115+
},
116+
filters: {
117+
tasks,
118+
search,
119+
},
120+
};
121+
}
122+
92123
//get all possible scheduled tasks
93124
const possibleTasks = await this._replica.backgroundWorkerTask.findMany({
94-
distinct: ["slug"],
95125
where: {
126+
workerId: latestWorker.id,
96127
projectId: project.id,
128+
runtimeEnvironmentId: environmentId,
97129
triggerSource: "SCHEDULED",
98130
},
99131
});
@@ -107,7 +139,7 @@ export class ScheduleListPresenter extends BasePresenter {
107139
taskIdentifier: tasks ? { in: tasks } : undefined,
108140
instances: {
109141
some: {
110-
environmentId: environments ? { in: environments } : undefined,
142+
environmentId,
111143
},
112144
},
113145
type: filterType,
@@ -168,13 +200,11 @@ export class ScheduleListPresenter extends BasePresenter {
168200
where: {
169201
projectId: project.id,
170202
taskIdentifier: tasks ? { in: tasks } : undefined,
171-
instances: environments
172-
? {
173-
some: {
174-
environmentId: environments ? { in: environments } : undefined,
175-
},
176-
}
177-
: undefined,
203+
instances: {
204+
some: {
205+
environmentId,
206+
},
207+
},
178208
type: filterType,
179209
AND: search
180210
? {
@@ -242,25 +272,19 @@ export class ScheduleListPresenter extends BasePresenter {
242272
};
243273
});
244274

245-
const limit = await getLimit(project.organizationId, "schedules", 100_000_000);
246-
247275
return {
248276
currentPage: page,
249277
totalPages: Math.ceil(totalCount / pageSize),
250278
totalCount: totalCount,
251279
schedules,
252-
possibleTasks: possibleTasks.map((task) => task.slug),
253-
possibleEnvironments: project.environments.map((environment) => {
254-
return displayableEnvironment(environment, userId);
255-
}),
280+
possibleTasks: possibleTasks.map((task) => task.slug).sort((a, b) => a.localeCompare(b)),
256281
hasFilters,
257282
limits: {
258283
used: schedulesCount,
259284
limit,
260285
},
261286
filters: {
262287
tasks,
263-
environments,
264288
search,
265289
},
266290
};

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.schedules/route.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,28 +93,21 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
9393
const url = new URL(request.url);
9494
const s = Object.fromEntries(url.searchParams.entries());
9595
const filters = ScheduleListFilters.parse(s);
96-
filters.environments = [environment.id];
9796

9897
const presenter = new ScheduleListPresenter();
9998
const list = await presenter.call({
10099
userId,
101100
projectId: project.id,
101+
environmentId: environment.id,
102102
...filters,
103103
});
104104

105105
return typedjson(list);
106106
};
107107

108108
export default function Page() {
109-
const {
110-
schedules,
111-
possibleTasks,
112-
possibleEnvironments,
113-
hasFilters,
114-
limits,
115-
currentPage,
116-
totalPages,
117-
} = useTypedLoaderData<typeof loader>();
109+
const { schedules, possibleTasks, hasFilters, limits, currentPage, totalPages } =
110+
useTypedLoaderData<typeof loader>();
118111
const location = useLocation();
119112
const organization = useOrganization();
120113
const project = useProject();

apps/webapp/app/routes/api.v1.schedules.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,9 @@ export async function loader({ request }: LoaderFunctionArgs) {
100100

101101
const result = await presenter.call({
102102
projectId: authenticationResult.environment.projectId,
103+
environmentId: authenticationResult.environment.id,
103104
page: params.data.page ?? 1,
104105
pageSize: params.data.perPage,
105-
environments: [authenticationResult.environment.id],
106106
});
107107

108108
return {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- CreateIndex
2+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "BackgroundWorker_runtimeEnvironmentId_createdAt_idx" ON "BackgroundWorker" ("runtimeEnvironmentId", "createdAt" DESC);

internal-packages/database/prisma/schema.prisma

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1627,6 +1627,8 @@ model BackgroundWorker {
16271627
16281628
@@unique([projectId, runtimeEnvironmentId, version])
16291629
@@index([runtimeEnvironmentId])
1630+
// Get the latest worker for a given environment
1631+
@@index([runtimeEnvironmentId, createdAt(sort: Desc)])
16301632
}
16311633

16321634
model BackgroundWorkerFile {

0 commit comments

Comments
 (0)