Skip to content

[ING-502] feat(alerts): add alert resolution columns - #6080

Merged
aquinofb merged 1 commit into
mainfrom
ing-502-alert-resolution-columns
Aug 7, 2026
Merged

[ING-502] feat(alerts): add alert resolution columns#6080
aquinofb merged 1 commit into
mainfrom
ing-502-alert-resolution-columns

Conversation

@aquinofb

@aquinofb aquinofb commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Context

Lago alerts only fire when usage or a wallet balance crosses a limit; they never signal the return to normal. A customer who automatically blocks something on an alert has no automatic way to release that block. This adds the schema for a new alert.resolved webhook that a threshold can opt into.

Nothing here is reachable yet. The feature stays inactive until the API opt-in ships, so this is groundwork.

Changes

  • notify_on on alert thresholds. It defaults to notifying on trigger only, so existing alerts behave exactly as today.
  • kind on alert events, distinguishing a real trigger from a recovery, and from a silent record written when a threshold already sits past its line at opt-in time.
  • in_alarm_thresholds and fully_resolved on alert events, populated only on recovery rows.
  • The three triggered_alerts associations are scoped to triggers, so readers whose name already promises triggers keep meaning that once recoveries exist. Alert also gains all_triggered_alerts for the full event stream, mirroring billing_entities and all_billing_entities on Organization.

Every column is added with a constant default, so these are metadata-only changes on PostgreSQL 15: no table rewrite, no long lock, and no back-fill.

Both alert serializers, the GraphQL threshold type and the export view all enumerate their fields explicitly, so none of the new columns reaches a customer-visible surface.

@aquinofb
aquinofb requested review from a team, mariohd and toommz and removed request for a team August 5, 2026 13:42
@aquinofb aquinofb self-assigned this Aug 5, 2026
Comment thread app/models/usage_monitoring/triggered_alert.rb
Comment thread app/models/usage_monitoring/alert_threshold.rb Outdated
@aquinofb
aquinofb force-pushed the ing-502-alert-resolution-columns branch 2 times, most recently from 790ae06 to 6765a55 Compare August 6, 2026 13:26
Lago alerts only signal when usage or a wallet balance crosses a limit;
they never signal the return to normal. Customers who block something
automatically on an alert have no automatic way to release that block.
The Alert Resolution feature adds an alert.resolved webhook that a
threshold can opt into, and this is its first step: the schema every
later part of the feature needs.

Nothing here is reachable by a customer. The feature stays inactive
until the API opt-in ships, so this lands purely as groundwork.

Alert thresholds gain notify_on, recording which transitions should
notify. It defaults to notifying on trigger only, so existing alerts
behave exactly as they do today.

The alert events table gains a kind, distinguishing a real trigger from
a recovery and from a silent record written when a threshold is already
past its line at opt-in time. It also gains the two fields a recovery
needs: which opted-in thresholds remain in alarm after the event, and
whether the alert is fully back to normal across all of its thresholds.
Both are nullable because only recovery rows carry them.

Every column is added with a constant default, so on PostgreSQL 15 these
are metadata-only changes with no table rewrite and no long lock, and no
back-fill is required.

The three triggered_alerts associations are scoped to triggers only, so
that readers whose name already promises triggers keep meaning that once
recoveries exist. This is deliberately done now, while every row is a
trigger and the scope is verifiably a no-op, rather than later when it
would be a behaviour change entangled with the recovery engine. Alert
also gains all_triggered_alerts for the full event stream, following the
same pattern as billing_entities and all_billing_entities on
Organization, since the recovery engine needs to read non-trigger rows.
@aquinofb
aquinofb force-pushed the ing-502-alert-resolution-columns branch from 6765a55 to 9d5b1df Compare August 6, 2026 14:01
@aquinofb
aquinofb merged commit 296436e into main Aug 7, 2026
12 checks passed
@aquinofb
aquinofb deleted the ing-502-alert-resolution-columns branch August 7, 2026 13:41
aquinofb added a commit that referenced this pull request Aug 12, 2026
…t view (#6083)

> Stacked on #6080, which adds the column this filters on. GitHub will
retarget this to `main` once that merges.

## Context

The alert events export view selects every row of the events table with
no filter. That table now also holds recovery events and silent seed
records, and the view exposes no column that would let a consumer tell
them apart, so without a filter they arrive downstream indistinguishable
from alerts that actually fired:

```mermaid
graph LR
    T["kind: triggered<br/>a real alert firing"] --> V1["view v01<br/>no filter"]
    R["kind: resolved<br/>a recovery"] --> V1
    S["kind: seeded<br/>silent record, no webhook"] --> V1
    V1 --> W1["consumer sees all three,<br/>indistinguishable"]

    T2["kind: triggered"] --> V2["view v02<br/>WHERE kind = triggered"]
    R2["kind: resolved"] -.blocked.-> V2
    S2["kind: seeded"] -.blocked.-> V2
    V2 --> W2["consumer sees<br/>real firings only"]
```

## Changes

Ships a new version of the view restricted to real triggers. The column
list is unchanged, so nothing a consumer already reads moves or
disappears; only rows that were never meant to be there are excluded. An
export of recovery events can follow separately if anyone needs one.

Plain equality is used rather than `IS DISTINCT FROM`. #5905 needed the
latter on `exports_fees_v03`, because that view joins to invoices so the
column is NULL for legitimate rows, but here the discriminator is NOT
NULL on the base table and the view has no join.

Adds the view's first spec: a trigger is exported, a recovery and a seed
record are not.

## Note for consumers of this view

This removes rows that would otherwise be emitted, so anything counting
rows from this view will see real firings only. Worth agreeing with the
people reading it before this merges rather than after.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants