perf(webapp): bound checkSchedule environment load to the requested ids - #4598
Conversation
CheckScheduleService.call loaded every environment of a project and then narrowed to the requested environmentIds via resolveProjectScopedEnvironments. On preview-heavy projects that meant loading hundreds of archived branch rows to validate one env, on a path called per-scheduled-task on deploy and from upsertTaskSchedule. Bound the environments relation load to boundedIn(environmentIds) so it returns <= the number of requested envs instead of the whole project. Foreign-id rejection and archived-branch rejection are both preserved: a requested id absent from the bounded result is still treated as foreign, and a requested archived branch still comes back with archivedAt set.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📜 Recent review details⏰ Context from checks skipped due to timeout. (18)
🧰 Additional context used📓 Path-based instructions (12)**/*.{ts,tsx}📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
{packages/core,apps/webapp}/**/*.{ts,tsx}📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
**/*.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
**/*.ts📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)
Files:
apps/webapp/**/*.{ts,tsx}📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Files:
apps/**/*.{ts,tsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
apps/webapp/app/**/*.{ts,tsx}📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Files:
apps/webapp/app/**/*.ts📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Files:
apps/webapp/app/v3/**/*.ts📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Files:
**/*.{test,spec}.{ts,tsx}📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
apps/webapp/**/*.test.{ts,tsx}📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Files:
apps/webapp/**/*.{test,spec}.{ts,tsx}📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Files:
🧠 Learnings (52)📓 Common learnings📚 Learning: 2026-07-26T13:14:02.968ZApplied to files:
📚 Learning: 2026-08-12T06:32:24.127ZApplied to files:
📚 Learning: 2026-05-14T14:54:39.095ZApplied to files:
📚 Learning: 2026-07-15T18:37:08.044ZApplied to files:
📚 Learning: 2026-07-30T18:44:03.619ZApplied to files:
📚 Learning: 2026-08-12T19:02:29.690ZApplied to files:
📚 Learning: 2026-07-26T00:55:37.444ZApplied to files:
📚 Learning: 2026-03-25T15:29:25.889ZApplied to files:
📚 Learning: 2026-03-25T15:29:25.889ZApplied to files:
📚 Learning: 2026-07-15T18:37:08.044ZApplied to files:
📚 Learning: 2026-06-06T09:22:01.985ZApplied to files:
📚 Learning: 2026-03-10T17:56:20.938ZApplied to files:
📚 Learning: 2026-03-22T13:26:12.060ZApplied to files:
📚 Learning: 2026-03-22T19:24:14.403ZApplied to files:
📚 Learning: 2026-05-18T08:21:27.694ZApplied to files:
📚 Learning: 2026-05-18T08:21:27.694ZApplied to files:
📚 Learning: 2026-06-13T19:53:13.759ZApplied to files:
📚 Learning: 2026-06-17T17:13:49.929ZApplied to files:
📚 Learning: 2026-06-23T13:04:21.413ZApplied to files:
📚 Learning: 2026-03-29T19:16:28.864ZApplied to files:
📚 Learning: 2026-06-09T16:27:26.195ZApplied to files:
📚 Learning: 2026-05-05T09:38:02.512ZApplied to files:
📚 Learning: 2026-05-12T21:04:05.815ZApplied to files:
📚 Learning: 2026-06-25T18:21:51.905ZApplied to files:
📚 Learning: 2026-07-03T17:10:21.498ZApplied to files:
📚 Learning: 2026-05-14T08:21:07.614ZApplied to files:
📚 Learning: 2026-06-04T18:16:35.386ZApplied to files:
📚 Learning: 2026-06-09T17:58:04.699ZApplied to files:
📚 Learning: 2026-07-30T18:43:56.874ZApplied to files:
📚 Learning: 2026-07-27T15:07:14.579ZApplied to files:
📚 Learning: 2026-04-15T15:39:31.575ZApplied to files:
📚 Learning: 2026-07-15T18:37:08.044ZApplied to files:
📚 Learning: 2026-08-08T17:53:11.238ZApplied to files:
📚 Learning: 2026-04-16T13:45:22.317ZApplied to files:
📚 Learning: 2026-04-07T14:12:18.946ZApplied to files:
📚 Learning: 2026-08-11T22:35:59.403ZApplied to files:
📚 Learning: 2026-07-18T18:31:37.633ZApplied to files:
📚 Learning: 2026-03-02T12:43:25.254ZApplied to files:
📚 Learning: 2026-07-18T18:17:16.266ZApplied to files:
📚 Learning: 2026-08-12T08:49:25.047ZApplied to files:
📚 Learning: 2026-07-18T18:17:07.979ZApplied to files:
📚 Learning: 2026-05-18T14:40:02.173ZApplied to files:
📚 Learning: 2026-07-26T20:37:56.220ZApplied to files:
📚 Learning: 2026-08-12T15:52:09.289ZApplied to files:
📚 Learning: 2026-08-08T08:33:22.847ZApplied to files:
📚 Learning: 2026-05-01T15:45:09.326ZApplied to files:
📚 Learning: 2026-08-11T21:56:48.387ZApplied to files:
📚 Learning: 2026-05-07T12:25:18.271ZApplied to files:
📚 Learning: 2026-05-28T20:02:10.647ZApplied to files:
📚 Learning: 2026-08-08T12:49:17.489ZApplied to files:
📚 Learning: 2026-06-16T09:19:47.637ZApplied to files:
🔇 Additional comments (3)
WalkthroughCheckScheduleService now limits project environment queries to requested environment IDs with 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
What
CheckScheduleService.callloaded every environment of a project ({ id, type, archivedAt }, no filter) and then immediately narrowed to just the requestedenvironmentIdsviaresolveProjectScopedEnvironments. It only ever uses the requested envs (to reject foreign env ids and reject archived branches). On a preview-heavy project that meant loading hundreds of archived branch rows to validate one, on a path called in a per-scheduled-task loop on the deploy path (createBackgroundWorker->syncDeclarativeSchedules) and fromupsertTaskSchedule.The query is index-backed and individually fast (rows_read/returned = 1 per predicate), so this is about result-set width / egress and wasted work at scale (~580k calls/24h observed via Insights), not a slow plan.
Change
Bound the
environmentsrelation load toboundedIn(environmentIds):Returns
<=the number of requested envs (usually 1) instead of the whole project. Both existing behaviors are preserved:resolveProjectScopedEnvironmentsreports it asforeign(a missing requested id is already treated as foreign).archivedAtset, so the downstreamCan't add or edit a schedule for an archived branchcheck still fires.archivedAtis kept in the select deliberately, so this bounds by id rather than filtering archived rows out.Evidence (isolated stack, seeded 1 prod env + 40 archived branch envs)
Local
EXPLAIN (ANALYZE)of the exact environments sub-select:id IN (requested))Rows Removed by Filter: 40)Same
RuntimeEnvironment_projectId_idx, no plan change. Rows to the client drop tolen(environmentIds), which is the point.Unit (vitest, testcontainers, real Postgres):
apps/webapp/test/checkSchedule.test.tsextended to prove, on real rows, that the bounded load returns only the requested env (1 of 10), still reports a foreign id as foreign, and still surfaces an archived branch when it is the requested one. 5/5 pass.Full e2e (both execution modes, real stack): a purpose-built project with two declarative
schedules.tasks.trigger dev: dev worker created, both schedules synced through the editedcheckScheduleloop, no errors.trigger deploy(managed deployment): PRODUCTION worker registered, both schedules synced against the prod environment through the same loop, prod + dev schedule instances active, no errors.typecheck --filter webappclean.Rollout / rollback
Straight deploy, no flag, no migration. Rollback is revert-only (read-path narrowing, no data change). Old and in-flight rows read correctly under both the old and new code.
Out of scope
The two lower-priority sibling reads in the ticket (the Query/metrics env id->slug map and the env-var repository fan-out) are left for follow-ups; they need caching / per-method scoping rather than this single bound.