Repository navigation
fix(alerts): v2 rules report last_evaluated_at for healthy rules - #2326
Conversation
…rors listRules only read state rows that carried an error, so healthy rules always reported a null last evaluation. The time is now the newest across all of a rule's group rows (anomaly __total__ heartbeats included); the error still comes from the newest row that has one. The document returned by a rule update (v2 PATCH) gets the same state, so list, retrieve and update agree.
Maple review🟢 Confidence 10/10 · safe to merge. Aggregation, both callers and the GET route that reads it, plus the new tests, all check out; nothing to fix. Makes
|
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
🍁 Maple PR previewNote Preview resources were removed when the preview label came off. Final commit |
Preview test (
|
| Check | Result |
|---|---|
New enabled error_rate rule, before its first evaluation |
last_evaluated_at: null from the create response, GET and the list |
| Same rule after its first evaluation (11:43:40) | the list, GET and two PATCH responses all return 2026-11-07T11:43:40.930Z, with last_evaluation_error: null |
| Scoped anomaly rule | null at creation, then 2026-11-07T11:44:41.599Z after its first tick, with no error |
| Disabled rule | keeps its last evaluation time |
| Cleanup | both rules and the test webhook destination deleted |
Not covered on the preview:
- A rule with an evaluation error: none of the test rules errored.
- The org's default anomaly rule: the
Previeworg doesn't have one. - The value moving forward on a later evaluation: each rule was evaluated once and then not again, and
last_scheduled_atstayed frozen. The preview scheduler was not re-running rules, which is separate from this change. The newest-row selection is covered by the unit tests.
Traces (deployment.environment = pr-2326)
| Span | n | Duration | Postgres queries |
|---|---|---|---|
AlertRulesService.listRules |
7 | 13 to 19 ms | 2: SELECT alert_rules, then SELECT alert_rule_states |
AlertRulesService.loadEvaluationStates |
9 | 4 to 7 ms | 1 |
GET /v2/alerts/rules, whole request |
6 | 13 to 120 ms |
The slow requests spend about 100 ms in OrganizationRegionService.ensureServedHere, a cold org lookup through the cache, before any rule code runs.
PATCH /v2/alerts/rules/:id (trace 81ae2a16117bfde337008d01bfe08ccd) takes 219 ms in total:
- 133 ms is the cold org region lookup.
listRulestakes 17 ms. That read is unchanged by this PR.upsertRuleRowtakes 57 ms. Its new per-rule state read, scoped to the rule's id, adds 7 ms.
There were no errors, retries or N+1 reads. The state read is one query per request, the same query the list already ran before this change.
The v2 rules API returned
last_evaluated_at: nullfor every healthy rule.AlertRulesServiceonly took the time from a rule-state row that carried an evaluation error.What changed
last_evaluated_atis now the newest evaluation across all of a rule's state rows. That includes the__total__heartbeat row that anomaly rules write every tick.Readers
Verification
AlertRulesService.test.ts, 5 new tests:__total__row reports that time;3 of these fail without the fix. The file passes 7/7.
apps/apiv2alerts.http.test.ts: a new wire test covers create, then list, GET and PATCH after an evaluation. 37/37 pass.Typecheck is clean in backend and api. Both oxlint configs are clean.
Open in [code]smith · Need help on this PR? Tag
@codesmith-botwith what you need.Summary by CodeRabbit