You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Deleting a ProjectAlertChannel fires the FK cascade DELETE FROM ONLY "ProjectAlert" WHERE $1 = "channelId". That cascade is scan-shaped: with no index on channelId, it reads the entire ProjectAlert table to find the few child rows belonging to the deleted channel. The parent DELETE ProjectAlertChannel does almost no work itself; its latency is dominated by this cascade. ProjectAlert is append-heavy and grows over time, so the scan cost only increases.
Diagnosis
ProjectAlert had no index on channelId (only pkey + a friendlyId unique). The cascade therefore did a full sequential scan of the whole table. The sibling ProjectAlertStorage cascade on the same delete is index-backed and stays fast, which isolates the missing index as the cause.
Change
Add @@index([channelId]) on ProjectAlert, created with CREATE INDEX CONCURRENTLY IF NOT EXISTS so prisma migrate deploy stays safe on a live table.
Benchmark (local, seeded)
Local Postgres seeded with 1,000,000 ProjectAlert rows across 50 channels (~20k rows per channel), EXPLAIN (ANALYZE, BUFFERS) on the cascade delete:
before
after
plan
Seq Scan (1M rows)
Bitmap Index Scan
direct child delete
740 ms
22 ms
parent delete ProjectAlert_channelId_fkey trigger
77.7 ms
23.8 ms
Expected impact
The cascade drops from a full-table sequential scan to a targeted index lookup. The win grows with the table: the more rows in ProjectAlert, the more a scan costs and the more the index saves, so the benefit is larger than the seeded numbers above.
Risks
One extra btree to maintain on every ProjectAlert insert; acceptable for a single-column index on a high-insert table, and it should be pre-created before the migration deploys (per the repo index rules).
No behavior change: no rows orphaned, no ordering or result-set change, read paths untouched.
Follow-up
ProjectAlert's other cascade FK columns (projectId, environmentId, workerDeploymentId) are also unindexed, but their parents are soft-deleted rather than physically removed, so those cascades do not currently fire. Lower priority unless a hard-delete path is introduced.
Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.
This PR includes no changesets
When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types
Adds an index on ProjectAlert.channelId to the Prisma schema. Adds a concurrent, idempotent migration to create the database index. Documents faster alert-channel deletion as alert history grows.
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name
Status
Explanation
Resolution
Description check
⚠️ Warning
The description explains the problem, diagnosis, change, benchmark, impact, risks, and follow-up, but it omits the required issue, checklist, changelog, and screenshots sections.
Add the template sections, including the issue reference, completed checklist, explicit testing steps, changelog entry, and screenshots or a statement that screenshots are not applicable.
✅ Passed checks (4 passed)
Check name
Status
Explanation
Docstring Coverage
✅ Passed
No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check
✅ Passed
Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check
✅ Passed
Check skipped because no linked issues were found for this pull request.
Title check
✅ Passed
The title clearly summarizes the primary database performance change and its effect on ProjectAlertChannel deletes.
✨ Finishing Touches🧪 Generate unit tests (beta)
Create PR with unit tests
Commit unit tests in branch feature/tri-13095-index-projectalertchannelid-so-alert-channel-deletes-stop
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why this change
Deleting a
ProjectAlertChannelfires the FK cascadeDELETE FROM ONLY "ProjectAlert" WHERE $1 = "channelId". That cascade is scan-shaped: with no index onchannelId, it reads the entireProjectAlerttable to find the few child rows belonging to the deleted channel. The parentDELETE ProjectAlertChanneldoes almost no work itself; its latency is dominated by this cascade.ProjectAlertis append-heavy and grows over time, so the scan cost only increases.Diagnosis
ProjectAlerthad no index onchannelId(onlypkey+ afriendlyIdunique). The cascade therefore did a full sequential scan of the whole table. The siblingProjectAlertStoragecascade on the same delete is index-backed and stays fast, which isolates the missing index as the cause.Change
Add
@@index([channelId])onProjectAlert, created withCREATE INDEX CONCURRENTLY IF NOT EXISTSsoprisma migrate deploystays safe on a live table.Benchmark (local, seeded)
Local Postgres seeded with 1,000,000
ProjectAlertrows across 50 channels (~20k rows per channel),EXPLAIN (ANALYZE, BUFFERS)on the cascade delete:ProjectAlert_channelId_fkeytriggerExpected impact
The cascade drops from a full-table sequential scan to a targeted index lookup. The win grows with the table: the more rows in
ProjectAlert, the more a scan costs and the more the index saves, so the benefit is larger than the seeded numbers above.Risks
ProjectAlertinsert; acceptable for a single-column index on a high-insert table, and it should be pre-created before the migration deploys (per the repo index rules).Follow-up
ProjectAlert's other cascade FK columns (projectId,environmentId,workerDeploymentId) are also unindexed, but their parents are soft-deleted rather than physically removed, so those cascades do not currently fire. Lower priority unless a hard-delete path is introduced.