Скорость сгорания бюджета ошибок — что тут не так?

Привет, %username%! В индустрии прижился термин «скорость сгорания бюджета ошибок» (Error Budget Burn Rate). Однако слово «скорость» здесь часто вводит в заблуждение, заставляя мозг искать привычные физические размерности там, где их нет.
Цена этой путаницы вполне материальна. Алерт, настроенный на абсолютную интенсивность ошибок, на ночном спаде трафика молчит весь инцидент, а на утреннем пике будит дежурного на ровном месте. Разберемся, почему Burn Rate — это в первую очередь безразмерный коэффициент, и как из него собирается алерт, который срабатывает по делу.
Начнем с математики#
Сейчас мы посчитаем два числа: сам бюджет ошибок и интенсивность, с которой его можно тратить. Ни одно из них не будет Burn Rate — и вот это как раз самое интересное.
Представим сервис со стабильной нагрузкой в 1 000 запросов в минуту (req/min).
Мы установили SLO = 99.9%.
- Определяем бюджет ошибок: \(\text{Error Budget} = 1 - \text{SLO} = 0.1\%\) (или \(0.001\) в долях).
- Считаем бюджет в абсолютных величинах:
- За 30-дневное окно общее количество запросов составит: \(30 \text{ дней} \times 24 \text{ часа} \times 60 \text{ минут} \times 1\,000 \text{ req/min} = 43\,200\,000 \text{ запросов}\).
- Допустимое количество ошибок: \(43\,200\,000 \times 0.001 = \mathbf{43\,200 \text{ ошибок}}\).
Это наш исчерпаемый ресурс на 30 дней.
Как можно тратить этот бюджет?#
Мы можем распределить эти 43 200 ошибок равномерно по всему месяцу. Чтобы понять «нормальную» интенсивность ошибок, разделим бюджет на время:
Это — Absolute Rate (абсолютный показатель). У него есть размерность (ошибки/мин), и он намертво привязан к текущему трафику: на ночном спаде в 100 req/min та же одна ошибка в минуту означает уже десятикратное превышение нормы. Полезное число, но это всё еще не Burn Rate.
Формула коэффициента#
Burn Rate считается не через абсолютные значения, а через доли:
Возьмем наш сервис с SLO 99.9% (допустимо 0.1% ошибок). Если за последние 5 минут мы поймали 2% ошибок:
Обрати внимание на то, что произошло с единицами измерения. И в числителе, и в знаменателе стоит одна и та же величина — доля неуспешных запросов от общего числа. При делении они сокращаются, и на выходе остается голое число. Не «двадцать чего-то в час», а просто двадцать раз.
Два понятия, которые часто путают#
Теперь, когда оба числа посчитаны, разница видна в упор.
| Характеристика | Absolute Error Rate (Абсолютный показатель) | Burn Rate (Коэффициент сгорания) |
|---|---|---|
| Что измеряет | Фактическую интенсивность ошибок «здесь и сейчас». | Во сколько раз текущее потребление превышает норму. |
| Размерность | Имеет единицы измерения (ошибок/сек, % ошибок). | Безразмерный множитель (\(1\times, 14.4\times, 50\times\)). |
| Вопрос | «Сколько ошибок произошло за последнюю минуту?» | «Если такая интенсивность сохранится, уложимся ли мы в бюджет?» |
| Интерпретация | Зависит от объема трафика в данный момент. | Универсальна: \(>1\) — бюджет тает слишком быстро, \(1\) — норма. |
Почему «скорость» — плохое слово?#
В физике скорость (\(v = s/t\)) всегда привязана к единицам времени (км/ч, м/с). Когда мы называем Burn Rate скоростью, мы подсознательно ищем время в знаменателе. А его там нет — оно сократилось еще на этапе деления долей.
На самом деле Burn Rate — это моментальный снимок соотношения, а не процесс, растянутый во времени.
- Burn Rate = 1: Мы тратим бюджет ровно с той интенсивностью, которая позволит исчерпать его в последнюю секунду периода (при условии, что такая интенсивность сохранится).
- Burn Rate = 20: При такой нагрузке месячный бюджет сгорит за полтора дня (\(30 / 20 = 1.5\)).
Откуда берется каноническое 14.4#
В статьях про алертинг постоянно всплывает множитель \(14.4\), и выглядит он как магическое число. На самом деле его выводят из простого требования: считать инцидентом ситуацию, когда сервис сжигает 2% месячного бюджета за один час.
Один час — это \(\tfrac{1}{720}\) часть 30-дневного окна. Значит, коэффициент равен доле потраченного бюджета, деленной на долю прошедшего времени:
Обратная проверка сходится: при постоянном коэффициенте \(14.4\) месячный бюджет заканчивается за \(30 / 14.4 \approx 2.08\) дня. Из той же арифметики получаются и остальные пороги: 5% бюджета за 6 часов дают множитель \(6\), а 10% за трое суток — множитель \(1\).
Главный аргумент: Burn rate — это не про то, «как быстро» что-то движется, а про то, «во сколько раз» мы отклонились от нормы потребления.
Важные нюансы#
1. Burn Rate возможен только при наличии бюджета#
Безразмерность — необходимое, но не достаточное условие. Для Burn Rate нужен исчерпаемый ресурс.
- Сравнение \(\text{Latency p99} / \text{SLA threshold}\) — это просто относительный коэффициент. Он не показывает сгорание ресурса, так как «время ожидания» само по себе не накапливается и не восстанавливается.
- Однако, если SLO определен как «% запросов быстрее 200 мс», то каждый медленный запрос — это «трата» из бюджета ошибок. В этом случае расчет через доли запросов превращает метрику в настоящий Burn Rate.
2. Скользящее окно и «восстановление» бюджета#
В отличие от денег на счету, бюджет ошибок в SRE обычно считается на скользящем окне (например, последние 30 дней). Это значит, что бюджет не просто сгорает, но и восстанавливается: когда старые инциденты (31-дневной давности) выходят за пределы окна, ваш доступный бюджет снова увеличивается.
3. Проблема малого трафика#
Burn Rate плохо работает на сервисах с низкой нагрузкой. Если у вас 1 запрос в час, то одна случайная ошибка даст Burn Rate в тысячи единиц, что приведет к ложному алерту. Для таких систем используют другие подходы (например, агрегацию за очень большие промежутки времени).
Что с этим делать на практике#
Первое — почини терминологию. Не используй слово «скорость», если хочешь подчеркнуть кратность превышения нормы; называй это коэффициентом сгорания или множителем. Звучит как придирка, но именно из-за слова «скорость» в дашборды регулярно уезжает абсолютная интенсивность ошибок вместо коэффициента.
Второе — собери на этом множителе алерты. Один порог не работает: низкий даст шум, высокий проспит медленную утечку бюджета. Поэтому берут несколько окон одновременно, и для каждого свой множитель:
| Доля бюджета | Окно | Burn Rate | Реакция |
|---|---|---|---|
| \(2\%\) | 1 час | \(14.4\times\) | будим дежурного |
| \(5\%\) | 6 часов | \(6\times\) | будим дежурного |
| \(10\%\) | 3 дня | \(1\times\) | заводим тикет |
К каждому длинному окну добавляют короткое (обычно \(\tfrac{1}{12}\) от длинного: 5 минут к часу, 30 минут к шести часам) — оно гасит алерт, когда всплеск уже закончился, и дежурный не разбирается с тем, что починилось само.
И вот здесь безразмерность из абстрактного свойства превращается в рабочий инструмент. Порог \(14.4\times\) одинаково валиден и для сервиса с миллионом RPS, и для сервиса с сотней RPS, и для ночного спада, и для черной пятницы. Один и тот же конфиг алерта переезжает между сервисами без пересчета абсолютных значений — ровно потому, что делить пришлось долю на долю, а время сократилось.
А как этот безразмерный множитель превращается в язык разговора с бизнесом про деньги — в статье «Надёжность строится в диалоге с бизнесом». Как эти пороги живут рядом с остальным алертингом и почему будят на симптом, а не на причину, — в листе про SLI-based alerting.