Repository navigation
Conversation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
Fixes #3508.
getPreviousAlertHistorieslooks up each group's newest AlertHistory row before every alert check. The{alert, group, createdAt}index is meant to make that one seek per group, but MongoDB fetched every row of the 7-day lookback for the alert. A 1-minute alert with ~1,300 groups writes ~1.9 M rows a day, so its check got slower every day. On our deployment that reached 4 minutes per run and an out-of-memory crash loop on catch-up.Two details of the pipeline prevented the per-group plan (
DISTINCT_SCAN): the compound$groupkey{alert, group}, and thestate: {$ne: ERROR}predicate, which the old index could not answer.This PR:
stateas the last field of the index:{alert: 1, group: 1, createdAt: -1, state: 1}. The ERROR exclusion becomes an index bound, so the planner keeps theDISTINCT_SCANand steps over ERROR keys without fetching those documents;state: {$ne: 'ERROR'}in$match. The only change is the$groupkey:_id: '$group'instead of{alert, group}, because a compound_idrules out theDISTINCT_SCAN;group || '') and uses only operators DocumentDB supports; callers are unchanged;{alert, group, createdAt}index at API startup (dropReplacedAlertHistoryIndexinrunStartupMigrations), after Mongoose has built the new one. Mongoose creates indexes but never drops them.explain()on 20 groups × 200 rows plus 200 group-less ERROR rows, the same on MongoDB 5.0.32 (CI) and 8.0.34:mainAt a larger scale (MongoDB 5.0.32): 1,500 groups × 300 one-minute evaluations, plus a group-less ERROR row every minute (450,300 rows):
mainexamines 450,300 docs in ~700 ms; this PR examines 1,500 docs and 1,800 keys in 10–18 ms. Over the full 7-day windowmaingrows with groups × evaluations (about 15 million docs for this alert); this PR stays at one doc per group, plus one key per ERROR row in the window.Index change (for operators)
{alert: 1, group: 1, createdAt: -1, state: 1}at startup (MongooseautoIndex), like the index added in perf: optimize AlertHistory queries (HDX-3706) #1928. On a large AlertHistory collection this takes a while. The build holds an exclusive lock only briefly at the start and the end, and the 30-day TTL bounds the collection's size. The API serves requests during the build.alert_1_group_1_createdAt_-1. Until then, checks keep using it. If the new index is missing (for example, its build failed), the old index is kept.autoIndex: falseand doesn't touch indexes; the API owns them.How to reproduce
Starting point:
make dev-int-style services (MongoDB and ClickHouse fromdocker-compose.ci.yml); no UI involved.On
main:state: 'OK', plus a group-less ERROR row every minute.explain('executionStats')on thegetPreviousAlertHistoriespipeline:FETCH > IXSCANwithtotalDocsExamined= 450,300.On this branch:
DISTINCT_SCAN,totalDocsExamined= 1,500.How to test on Vercel preview
N/A — non-UI change.
Tests
getPreviousAlertHistoriesreturns the newest non-ERROR row per group, with ERROR rows with and without a group, a group that stopped reporting while in ALERT, rows without a group (OK, then a newer ERROR), and a group with only ERROR rows. It passes onmaintoo: same results.main's index (4,200 docs examined vs ≤ 21).dropReplacedAlertHistoryIndexdrops the old index once the new one exists, is idempotent, and keeps the old index while the new one is missing.References
getPreviousAlertHistoriesreads every history row of the last 7 days, so check-alerts slows down and runs out of memory as history grows #3508Written with help from an AI assistant (Claude Code). The numbers come from the integration test setup on
mainand on this branch.