From 326e9950f439612a92aed526866441de82cfbb86 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Wed, 12 Aug 2026 08:16:55 +0100 Subject: [PATCH] perf(webapp): scope declarative schedule sync to the current environment (#4577) ## Summary Background worker registration runs on every deploy and every `trigger dev` file save. Its declarative-schedule reconcile loaded every declarative schedule for the whole project across all environments, then re-fetched the deletion candidates it already had in memory. For projects with many scheduled tasks or many environments, that meant reading tens of thousands of rows on each registration. This scopes the load to the environment being registered, drops the redundant re-fetch, and selects only the columns the reconcile needs. It also fixes the schedule-limit count (`getUsedSchedulesCount`), which joined `TaskSchedule` and `RuntimeEnvironment` without a project constraint and could scan those tables in full. Pushing `projectId` onto both joins gives it a project-scoped index path with the same result. Follow-up to [#4522](https://github.com/triggerdotdev/trigger.dev/pull/4522), which batched the delete side of the same reconcile. --- .../scope-declarative-schedule-sync.md | 6 ++++ .../app/v3/services/checkSchedule.server.ts | 2 ++ .../services/createBackgroundWorker.server.ts | 29 +++++++++++-------- 3 files changed, 25 insertions(+), 12 deletions(-) create mode 100644 .server-changes/scope-declarative-schedule-sync.md diff --git a/.server-changes/scope-declarative-schedule-sync.md b/.server-changes/scope-declarative-schedule-sync.md new file mode 100644 index 000000000..feb4050d3 --- /dev/null +++ b/.server-changes/scope-declarative-schedule-sync.md @@ -0,0 +1,6 @@ +--- +area: webapp +type: improvement +--- + +Make background worker registration cheaper for projects with many scheduled tasks by scoping declarative schedule reconciliation to the current environment and dropping redundant schedule lookups. diff --git a/apps/webapp/app/v3/services/checkSchedule.server.ts b/apps/webapp/app/v3/services/checkSchedule.server.ts index 0115c7420..bc28ee9c3 100644 --- a/apps/webapp/app/v3/services/checkSchedule.server.ts +++ b/apps/webapp/app/v3/services/checkSchedule.server.ts @@ -131,12 +131,14 @@ export class CheckScheduleService extends BaseService { projectId, active: true, environment: { + projectId, type: { not: "DEVELOPMENT", }, archivedAt: null, }, taskSchedule: { + projectId, active: true, }, }, diff --git a/apps/webapp/app/v3/services/createBackgroundWorker.server.ts b/apps/webapp/app/v3/services/createBackgroundWorker.server.ts index 25913230b..dc5c79129 100644 --- a/apps/webapp/app/v3/services/createBackgroundWorker.server.ts +++ b/apps/webapp/app/v3/services/createBackgroundWorker.server.ts @@ -655,9 +655,21 @@ export async function syncDeclarativeSchedules( where: { type: "DECLARATIVE", projectId: environment.projectId, + instances: { + some: { + environmentId: environment.id, + }, + }, }, - include: { - instances: true, + select: { + id: true, + friendlyId: true, + taskIdentifier: true, + instances: { + select: { + environmentId: true, + }, + }, }, }); @@ -764,16 +776,9 @@ export async function syncDeclarativeSchedules( //Delete instances for this environment //Delete schedules that have no instances left - const potentiallyDeletableSchedules = await prisma.taskSchedule.findMany({ - where: { - id: { - in: boundedIn(Array.from(missingSchedules)), - }, - }, - include: { - instances: true, - }, - }); + const potentiallyDeletableSchedules = existingDeclarativeSchedules.filter((schedule) => + missingSchedules.has(schedule.id) + ); const scheduleIdsToDelete: string[] = []; const scheduleIdsToDetachFromEnvironment: string[] = [];