[HDX-4997] Record alert evaluation errors in AlertHistory and add alert detail page - #2786
[HDX-4997] Record alert evaluation errors in AlertHistory and add alert detail page#2786wrn14897 wants to merge 3 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: bd81e59 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
🔴 Tier 4 — CriticalTouches authentication, tenancy data models, the public API or shipped database config — or substantially changes background tasks, the OTel pipeline, image build, or release CI. Why this tier:
Review process: Deep review from a domain expert. Synchronous walkthrough may be required. Stats
|
Greptile SummaryThis PR persists alert evaluation failures and introduces an alert-detail experience.
Confidence Score: 4/5The PR is not yet safe to merge because valid same-minute ranges can still suppress the evaluation-history request while the chart renders that range. The evaluations hook floors both picker bounds to minute boundaries and then requires the normalized start to precede the normalized end, so an exact range contained within one minute remains disabled rather than loading its evaluation history. Files Needing Attention: packages/app/src/api.ts
|
| Filename | Overview |
|---|---|
| packages/api/src/controllers/alertHistory.ts | Adds grouped evaluation reads, bounded cursor pagination, error aggregation, analytics resolution, and ERROR-aware transition handling. |
| packages/api/src/routers/api/alerts.ts | Exposes the team-scoped evaluations endpoint with validated and retention-clamped range parameters. |
| packages/api/src/tasks/checkAlerts/index.ts | Records evaluation failures and analytics while preserving retry and backfill behavior. |
| packages/api/src/tasks/checkAlerts/providers/default.ts | Persists and clears per-window ERROR history records alongside normal alert-state updates. |
| packages/app/src/AlertDetailPage.tsx | Composes the alert detail chart, exact-range controls, history strip, and paginated evaluation stream. |
| packages/app/src/api.ts | Adds alert evaluation fetching and pagination, but still collapses valid same-minute ranges before deciding whether to issue the request. |
| packages/app/src/components/alerts/AlertEvaluationsTable.tsx | Renders evaluation windows, grouped rows, analytics, failures, infinite loading, and explicit retry states. |
Sequence Diagram
sequenceDiagram
participant Job as Alert evaluation job
participant CH as ClickHouse
participant Mongo as AlertHistory
participant API as Evaluations API
participant UI as Alert detail page
Job->>CH: Evaluate alert query
alt Evaluation succeeds
Job->>Mongo: Persist state rows and analytics
else Query or notification fails
Job->>Mongo: Upsert ERROR row and error details
end
UI->>API: Request bounded time-range page
API->>Mongo: Group history by evaluation window
Mongo-->>API: States, groups, errors, analytics
API-->>UI: Page plus nextBefore cursor
Reviews (23): Last reviewed commit: "feat(alerts): persist evaluation errors ..." | Re-trigger Greptile
E2E Test Results✅ All tests passed • 276 passed • 1 skipped • 1114s
Tests ran across 4 shards in parallel. |
|
<!-- deep-review --> Deep Review🔴 P0/P1 — must fix
🟡 P2 — recommended
🔵 P3 nitpicks (7)
Reviewers (13): correctness, adversarial, security, reliability, api-contract, performance, testing, maintainability, kieran-typescript, data-migrations, julik-frontend-races, project-standards, agent-native. Testing gaps:
Coverage limitations: |
|
<!-- deep-review --> Deep Review🔴 P0/P1 — must fix
🟡 P2 — recommended
🔵 P3 nitpicks (10)
Reviewers (11): correctness, adversarial, security, reliability, api-contract, performance, testing, maintainability, kieran-typescript, julik-frontend-races, project-standards. Testing gaps:
Reviewer note: |
|
Scope note: 🔴 P0/P1 — must fix
🟡 P2 — recommended
🔵 P3 nitpicks (15)
Reviewers (12): correctness, adversarial, security, reliability, performance, api-contract, kieran-typescript, testing, maintainability, project-standards, agent-native, learnings-researcher. Verified as sound (no findings): team scoping on the new endpoint via Testing gaps:
|
34e82e6 to
9bea16d
Compare
3a29e53 to
c4cc636
Compare
9bea16d to
72cf980
Compare
Deep Review
✅ No critical issues found. 🟡 P2 -- recommended
🔵 P3 nitpicks (2)
Reviewers (3): correctness (orchestrator direct verification), agent-native, learnings-researcher. Testing gaps:
|
…up windows (HDX-4997) AlertHistory read-side support for the alert detail page: - Types (common-utils) for evaluation errors (AlertError/AlertErrorType incl. QUERY_TIMEOUT), per-window evaluations with per-group breakdown (capped at ALERT_EVALUATION_GROUPS_LIMIT, firing-first), and evaluation analytics (queryDurationMs, webhookDurationMs, backfilledBuckets). - AlertHistory schema gains optional errors + analytics fields, and AlertState gains ERROR (only ever used on history rows). - GET /alerts/:id/evaluations: per-window evaluation history scoped to a startTime/endTime range (clamped to the 31d retention window), grouped across group-by groups newest-first with a hard-bounded scan of at most ~(limit+1) intervals per request and a server-provided nextBefore cursor that always advances past the scanned slice, so paging progresses across gaps instead of stalling. - Windows with ERROR rows surface their errors (deduped, newest-first) and rank as ERROR; firing-transition annotations exclude ERROR rows. Nothing writes ERROR rows or analytics yet — the alert task's write side lands separately.
72cf980 to
9baf3f3
Compare
5368ba6 to
9baf3f3
Compare
Datadog-style alert status page at /alerts/:id, reachable via a Details link on each alerts-page row (the alert name keeps linking to its saved search / dashboard tile): - Header with state badge, silence/ack, source link, and a time picker; the alert's underlying query charted over the selected range with threshold reference lines and firing/recovery annotations. - Widened evaluation-history strip (60 windows); chart, strip, and event stream all follow the picker's exact range. - Evaluation event stream: one parent row per window labeled with the evaluated bucket start (matching the chart's x-axis), state, latest value, breaches, backfilled buckets, query/webhook durations, and error labels; per-group child rows for group-by alerts; older windows load via an infinite-scroll sentinel using the endpoint's nextBefore cursor. A failed page fetch unmounts the sentinel (whose effect would otherwise refire forever) and renders an explicit retry affordance; a failed initial page shows a failure message instead of the empty state. - Alerts-page history strip renders errored evaluation windows as striped-red segments with full error details in a modal on click. - The Details link and /alerts/:id route are gated behind NEXT_PUBLIC_ENABLE_ALERT_DETAILS (default off) — enabled in dev (.env.development) and CI (e2e webserver) only while the feature bakes; self-hosted deployments opt in via docker-compose. Includes unit tests and full-stack e2e coverage seeded directly in MongoDB.
… (HDX-4997) When an alert evaluation fails (ClickHouse query error/timeout, webhook failure), the only persisted signal was alert.executionErrors — a latest-only snapshot wiped by the next successful run. - Failed evaluations are recorded as ERROR-state AlertHistory rows carrying error type/message/timestamp, upserted per evaluation window so per-tick retries collapse into a single row; rows expire with the existing 30d TTL. - Webhook/notification failures also produce an ERROR row alongside the normal evaluation rows; a stale ERROR row from a failed earlier tick is removed when a clean same-window retry succeeds. - Retry/backfill semantics are untouched: ERROR rows are excluded from the due-ness gate, the retry date-range computation, and consecutive-window counting — recording an error never marks the window as evaluated, so the failed window is still retried every tick and backfilled on recovery. - Query timeouts are classified as QUERY_TIMEOUT (client request timeout/abort, server-side TIMEOUT_EXCEEDED/159, socket timeouts — walking the cause chain since the query client wraps failures) with an actionable message that includes the configured evaluation timeout. - Evaluation analytics (queryDurationMs, webhookDurationMs, backfilledBuckets) are recorded on every history row the evaluation writes, including ERROR rows.
78111b0 to
bd81e59
Compare
Summary
When an alert evaluation fails (ClickHouse query error/timeout, webhook failure), the only persisted signal was
alert.executionErrors— a latest-only snapshot wiped by the next successful run. Users couldn't see when evaluations failed, how often, or why. This is a prerequisite for ever tightening the alert query evaluation timeout (HDX-4977): before that value can change, users need to be able to see which alerts hit query errors/timeouts and when.This PR makes alert failures first-class, Datadog-monitor-style, and gives every alert a status page. Three logical commits:
1. Evaluations read model (
feat(alerts): evaluations read model)AlertError/AlertErrorTypeincl.QUERY_TIMEOUT), per-window evaluations with per-group breakdown, and evaluation analytics;AlertHistorygains optionalerrors/analyticsfields andAlertState.ERROR(only ever used on history rows).GET /alerts/:id/evaluations: per-window evaluation history scoped to astartTime/endTimerange with layered hard caps — span clamped to the 31d retention window,limitfixed at 200, each request scans a hard-bounded slice of at most ~(limit+1) × intervalof history (group-by alerts can have many rows per window and the$groupstage processes every matched row, so the scan is bounded, not just the returned page), and cursor-based paging via a server-providednextBeforethat always advances past the scanned slice so pagination keeps progressing across gaps instead of stalling.2. Alert detail page (
feat(app): alert detail page) — gated behindNEXT_PUBLIC_ENABLE_ALERT_DETAILSNew
/alerts/:idpage reachable via a Details link on each alerts-page row (the alert name keeps linking to the saved search / dashboard tile):k/n groups firing, firing-first, capped server-side at 50 with an explicit cap message); older windows load via an infinite-scroll sentinel. A failed page fetch unmounts the sentinel (whose effect would otherwise refire forever against a failing endpoint) and renders an explicit retry affordance.3. Error + analytics persistence (
feat(alerts): persist evaluation errors and analytics)ERROR-stateAlertHistoryrows carrying error type/message/timestamp, upserted per evaluation window so per-tick retries collapse into a single row (a permanently failing 1d alert produces 1 row/day, not 1/minute; rows expire with the existing 30d TTL). Webhook failures also produce an ERROR row alongside the normal rows; a stale ERROR row from a failed earlier tick is removed when a clean same-window retry succeeds.QUERY_TIMEOUT(client request timeout/abort, server-sideTIMEOUT_EXCEEDED/159, socket timeouts — walking the errorcausechain, sinceBaseClickhouseClient.querywraps failures and keeps the identifying metadata only oncause) with an actionable message that includes the configured evaluation timeout.queryDurationMs— time-to-failure on query-error rows,webhookDurationMs— total delivery wall time incl. retries,backfilledBuckets— missed ticks caught up in this run) are recorded on every history row the evaluation writes.Screenshots or video
!iconHow to test on Vercel preview
N/A — alert evaluation history requires the alerting job + MongoDB-backed alerts, which don't exist in LOCAL_MODE previews (and the detail page is flag-gated off there). Covered by API integration tests and full-stack Playwright tests instead.
How this was tested
tsc --noEmit(app + api); app unit tests (evaluations table incl. retry affordance, history cards, error classification — 18 classifier tests incl. wrapped/nested/self-referential cause chains)QUERY_TIMEOUTclassification, failed-window-still-retried/backfilled, webhook-failure ERROR rows (incl. grouped alerts), evaluations endpoint (range clamping, bounded scan,nextBeforecursor, per-group cap), analytics fieldsmake dev-e2e FILE=alerts): the 3 new specs (errored strip segment, error details modal, detail-page navigation + evaluation history) pass with the flag threaded through the e2e webserver; remainingAlert Creation/Lifecycle/Notesfailures reproduce identically on pristineorigin/main(21 suite-wide failures there) and are unrelatedReferences
Screenshot