Command Palette

Search for a command to run...

Hectal
Case 4.3·Alerts & SLOsSEV2

“The 5% threshold paged us for a 90-second blip — and stayed silent while a 1% error rate ate the whole month's budget in three days.”

case name: Too noisy and too slow at the same time

Service
alerting · shoplite checkout
Impact
Error budget fully spent without a single page
Detected by
The SLO dashboard, days too late
Time to resolve
Rules rewritten in an afternoon

Skills you'll use on this case

burn ratemulti-window, multi-burn-rate alertsshort windows to reset fasttickets for slow burns

00 It starts

Reproduce this incident in your lab, then work the case alongside the timeline below. Try each query yourself before reading its result.

terminal
$ curl -s -X POST localhost:8080/admin/chaos -H 'content-type: application/json' -d '{"scenario":"errors"}'
── lab output ──
{"active":["errors"]}
20% of checkouts failing is a burn rate of 40× against a 0.5% budget: the fast-burn page should fire within ~2 minutes.

01 The investigation

  1. Day 1

    FINDING

    A deploy causes 1% errors — below the 5% threshold

    The static alert never fires. 1% is twice the SLO's allowed 0.5%, which quietly burns budget at 2× speed.

    checkout error ratio vs static thresholderror ratio
    0.000.030.06static alert 5%day 1day 4
  2. Day 3

    REPORT

    The SLO dashboard shows the budget gone

  3. Day 5

    ALERT

    Meanwhile: a 90-second spike to 30% errors during a DB failover pages the on-call at 03:12

    It resolved itself before anyone opened a laptop, having used about 0.1% of the monthly budget. The static threshold treats a huge but tiny event as urgent and a small but relentless one as fine. Both are backwards.

  4. Day 6

    RESOLVED

    Replaced with multi-window burn-rate alerts

Root cause

A fixed error-ratio threshold measures how bad things are RIGHT NOW, not how fast the error budget is being consumed. It pages on short, harmless spikes and misses sustained, low-level failures, which is the opposite of what an SLO-driven team needs.

02 The concepts behind it

Burn rate

Burn rate = current error ratio ÷ allowed error ratio (1 − SLO). A burn rate of 1 spends exactly the whole budget over the SLO window. At 2 the budget is gone in half the window (14 days for 28). At 14.4 it's gone in about 2 days, so one hour of it costs ~2% of the monthly budget. Burn rate makes every alert a statement about the SLO: 'at this pace, we run out in N hours'.

Multi-window, multi-burn-rate

From Google's SRE workbook. PAGE if burn rate > 14.4 over the last 1 h AND over the last 5 min (a fast, significant burn that's still happening). PAGE if > 6 over 6 h AND over 30 min (a medium burn). TICKET if > 1 over 3 days AND over 6 h (a slow burn that will breach the SLO).

The LONG window gives significance: enough budget has actually been spent to matter. The SHORT window makes the alert stop quickly once the problem is fixed, instead of firing for an hour after recovery. The 90-second failover spike never sustains the long window, so it never pages; the 1% error rate trips the slow-burn ticket within hours.

Precision, recall, detection time, reset time

Every alert trades off four things: precision (does firing mean real impact?), recall (does real impact always fire?), detection time, and reset time. Static thresholds are poor on at least two. Burn-rate pairs get good scores on all four, which is why they became the standard for SLO alerting.

03 The fix

  1. 01Burn-rate alert rules on top of the recorded SLIs

    0.005 is the budget (1 − 0.995). The fast and medium burns page; the slow burn creates a ticket. The 3-day window uses increase-based math for brevity; in production, record a 3d ratio too.

    obs-lab/prometheus/slo.rules.ymladd to fileyaml
      - name: slo-checkout-alerts
        rules:
          - alert: CheckoutErrorBudgetFastBurn
            expr: |
              slo:checkout_errors:ratio_rate1h  > (14.4 * 0.005)
              and
              slo:checkout_errors:ratio_rate5m  > (14.4 * 0.005)
            labels: { severity: critical, service: shoplite, slo: checkout-availability }
            annotations:
              summary: "Checkout burning error budget at {{ $value | humanizePercentage }} errors (>14.4× budget) — ~2% of monthly budget per hour"
          - alert: CheckoutErrorBudgetMediumBurn
            expr: |
              slo:checkout_errors:ratio_rate6h  > (6 * 0.005)
              and
              slo:checkout_errors:ratio_rate30m > (6 * 0.005)
            labels: { severity: critical, service: shoplite, slo: checkout-availability }
            annotations:
              summary: "Checkout burning error budget at >6× — budget exhausted in under 5 days at this rate"
          - alert: CheckoutErrorBudgetSlowBurn
            expr: |
              (
                sum(increase(http_request_duration_seconds_count{job="shoplite", route="/checkout", status=~"5.."}[3d]))
                / sum(increase(http_request_duration_seconds_count{job="shoplite", route="/checkout"}[3d]))
              ) > 0.005
              and
              slo:checkout_errors:ratio_rate6h > 0.005
            labels: { severity: warning, service: shoplite, slo: checkout-availability }
            annotations:
              summary: "Checkout error rate above SLO for days — on track to breach"
    terminal
    $ curl -s -X POST localhost:9090/-/reload
    sleep 150 && curl -s localhost:9090/api/v1/alerts | jq -r '.data.alerts[] | .labels.alertname + " " + .state'
    ── expected output ──
    CheckoutErrorBudgetFastBurn firing
    CheckoutErrorRatioHigh firing
  2. 02Retire the static threshold

    Delete CheckoutErrorRatioHigh from alerts.yml. The burn-rate rules cover it with better precision and recall. Then switch the scenario off and watch the fast-burn alert RESOLVE within ~5 minutes, thanks to the short window.

    terminal
    $ curl -s -X POST localhost:8080/admin/chaos -H 'content-type: application/json' -d '{"scenario":"errors","enabled":false}'
    ── expected output ──
    {"active":[]}

04 Make sure it never surprises you again

  1. 01Apply the same pattern to latency

    A latency SLO ('99% of checkouts under 1 s') gets the same treatment, with the SLI from the le="1" bucket. Once you have the pattern, every SLO gets alerts for free. Tools like Sloth or Pyrra generate these rules from a short SLO spec.

05 Your turn: write the query

Run each one against your lab before revealing the answer. Share your own version in the comments; there's usually more than one correct query.

01

What's the current burn rate of the checkout availability SLO, as a single number?

02

At a burn rate of 14.4 sustained for one hour, what fraction of a 28-day budget is consumed?

06 Interview questions from this case

01

What is a burn-rate alert and why is it better than a static error-rate threshold?

02

Why does each burn-rate alert use two windows?

0/4 · 0%