Skip to content

Repository files navigation

otel-slo-burnmeter

A multi-window multi-burn-rate SLO alerting engine over OpenTelemetry-style spans, with an honest measured comparison against a naive threshold: the naive alert detects a severe regression a few minutes sooner, but raises three false pages on healthy spiky traffic where multi-window raises zero. The trade-off is precision, not speed, and the benchmark proves it both ways.

CI Coverage License Alerting

What this solves

  • SLO alerts must catch real regressions without paging on every transient spike, and a single threshold cannot do both; the measured result is that a short-window threshold false-pages three times on healthy spiky traffic.
  • Multi-window multi-burn-rate alerting uses a fast and a slow window together, so a spike never sustains the slow window and a real burn clears both; it raises zero false pages on the same trace.
  • The usual pitch for burn-rate alerting is "it detects faster", and this repo measures that claim honestly and finds it false for a well-tuned threshold; the real, defensible value is false-alarm suppression.

Why this exists

Every team that adopts SLOs writes an alert, and the first version is almost always "page me when the error rate is over 5% for 15 minutes". It works until the on-call gets paged at 3 a.m. for a two-minute blip that self-resolved, learns to ignore the alert, and then misses the real regression a month later. The single threshold is trying to serve two goals with one knob: shorten the window and it flaps, lengthen it and it is slow. Alert fatigue, not detection latency, is what actually kills SLO programs.

Multi-window multi-burn-rate alerting (from the Google SRE workbook) resolves the tension by requiring a fast window and a slow window to both exceed a burn-rate threshold before firing. The burn rate is computed from good/bad event counts, not averaged latency (ADR-002): a request is bad if it errored or crossed the latency threshold, and burn rate is the bad fraction divided by the error budget, so burn 14.4 means a 30-day budget is spent in about two days. Two tiers page on fast burns (14.4x on 5-minute and 1-hour windows) and ticket on slow burns (6x on 30-minute and 6-hour windows). The slow window is the false-positive filter; the fast window is the responsiveness.

The engine replays a seeded span trace minute by minute and evaluates both strategies, and the comparison is deliberately run across three regimes so the result is honest rather than cherry-picked: a severe regression, a slow ramp, and healthy-but-spiky traffic. The headline number is not detection speed (the naive threshold wins that); it is that multi-window raises zero false pages where the naive alert raises three.

The comparison

Comparison

Left: a severe regression at minute 120, both alerts fire almost immediately (naive one minute sooner). Right: healthy traffic with periodic two-minute spikes and no real regression, the naive threshold pages three times (red), multi-window never fires. Raw numbers in benchmark/results/comparison.json.

Tech stack

Technology Role in this project Why chosen here
Python 3.11 + NumPy Burn-rate math over span windows Vectorized window aggregation; the full 6-hour replay runs in under a second
Good/bad event model The SLI Catches tail failures an averaged latency would hide (ADR-002)
Multi-window burn-rate The alerting policy The precision the benchmark measures; two tiers separate page from ticket
FastAPI Span ingest + live /burn Same evaluator behind a service the way a collector sink would use it
Seeded 3-regime trace Honest benchmark Severe, slow, and spiky, so the comparison cannot be cherry-picked
pytest + pytest-cov Suite 10 tests including the false-alarm trade-off and the honest "naive is not slower" finding; 89 percent measured
GitHub Actions CI Lint, tests, and a replay that re-asserts the false-alarm invariant

Quickstart

Prerequisites: Python 3.11+, git.

git clone https://github.com/<you>/otel-slo-burnmeter.git
cd otel-slo-burnmeter
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

pytest --cov=burnmeter                 # test suite

for s in severe spikes slow; do python demo/generate_trace.py --seed 5 --scenario $s --out demo/trace_$s.json; done
python -c "
from burnmeter.config import Settings
from burnmeter.replay import load_trace, replay
for s in ('severe','spikes','slow'):
    r = replay(load_trace(f'demo/trace_{s}.json'), Settings(_env_file=None))
    print(s, 'mw_false', r['multiwindow_false_alarms'], 'naive_false', r['naive_false_alarms'])"

The numbers

Measured on the seeded traces (benchmark/results/comparison.json), SLO 99.5%, latency threshold 300ms:

Regime Multi-window detection lag Naive detection lag Multi-window false pages Naive false pages
Severe regression 8 min 1 min 0 0
Slow ramp 89 min 52 min 0 0
Healthy spikes (no regression) n/a (never fires) n/a 0 3

The honest reading: the naive threshold detects real regressions as fast or faster. Multi-window's win is the last row, three false pages eliminated. On a real on-call rotation, that is the difference between an alert people trust and one they mute.

Architecture decisions

ADR-001: multi-window over a single threshold, with the measured trade-off (precision bought at a few minutes of latency) stated plainly. ADR-002: burn rate from good/bad event counts rather than averaged latency, and why averages hide tail failures.

Intentionally out of scope

  • A real OTLP receiver. The engine consumes span records; wiring an OpenTelemetry collector exporter to /spans is mechanical and additive.
  • Alert routing (PagerDuty, Slack). The engine emits a tier decision; routing is a downstream concern.
  • Adaptive thresholds. Burn thresholds are the workbook's fixed values; auto-tuning them per service is future work that needs historical incident labels.

Security and compliance

Spans carry latency and an error flag, no payloads or user data. SLO parameters (target, threshold, window) are environment config and printed in the burn state. The engine is read-only over its ingested spans.

Failure modes

Failure Detection Behaviour Recovery
Transient spike on healthy traffic Slow window stays under threshold No page (the whole point) Tested: 0 false pages
Severe sustained burn Both windows clear threshold fast Page tier fires Tested; detection lag measured
Slow ramp regression Ticket tier eventually fires Ticket, not page (correct: slow burn) Measured lag; naive is faster here and the README says so
Sparse traffic (empty window) burn_rate returns 0 on no data No spurious alert on a quiet minute Guarded and tested
Latency threshold misconfigured Burn rate shifts Threshold printed in /burn Config knob; a product decision

Hardest problem solved

I set out to prove the standard claim that burn-rate alerting detects regressions earlier than a naive threshold, built the trace, ran the replay, and measured the opposite: the naive 5%-over-15-minutes alert fired at minute 172 on my slow ramp, while multi-window did not page until 209. My first instinct was that my windows were misconfigured, and I spent a while convinced there was a bug. There was not. A short averaging window genuinely detects a rising error rate faster than a burn-rate alert that has to accumulate signal in a one-hour window; that is arithmetic, not a defect.

The real bug was in my framing, not my code. I had picked the one scenario and the one naive tuning where the marketing claim happens to be defensible, and even there it was not true. The honest move was to test the regimes where the two strategies actually differ, which meant building three traces (severe, slow, spiky) and measuring both detection lag and false-alarm rate. That surfaced the finding that matters: on healthy spiky traffic the naive threshold raises three false pages and multi-window raises none, and a test now pins exactly that (test_spikes_multiwindow_suppresses_false_alarms), alongside a test that pins the uncomfortable truth (test_severe_naive_not_slower_than_multiwindow). The lesson I keep from it: when your measurement contradicts the claim you set out to prove, the measurement is usually right, and the interesting result is the one you did not expect. A benchmark that only tests the flattering case is marketing; testing the cases where your approach loses is what makes the case where it wins believable.

Future work

  • OTLP receiver wiring so a real collector feeds /spans.
  • Per-service burn thresholds learned from historical incident labels.
  • A third alerting strategy (EWMA) in the same replay for a three-way comparison.
  • Alert routing with dedup so the two tiers map to page vs ticket destinations.
  • First metric to watch in adoption: pages-per-week and their acknowledged-actionable rate. The whole point is that the second number stays near 100 percent.

License

MIT

About

Multi-window multi-burn-rate SLO alerting over spans, with an honest 3-regime benchmark: a naive threshold detects real regressions faster, but raises 3 false pages on healthy spiky traffic where multi-window raises 0. The value is precision, not speed, and the benchmark proves it both ways.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages