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

Edit...
Схема: физическая скорость v = s / t слева, дробь burn rate в центре, где единицы сокращаются и остаётся множитель 20×, справа — за сколько сгорает 30-дневный бюджет при кратности 1×, 14.4× и 20×
Создал «Мишка на сервере» · AI-assisted

Привет, %username%! В индустрии прижился термин «скорость сгорания бюджета ошибок» (Error Budget Burn Rate). Однако слово «скорость» здесь часто вводит в заблуждение, заставляя мозг искать привычные физические размерности там, где их нет.

Цена этой путаницы вполне материальна. Алерт, настроенный на абсолютную интенсивность ошибок, на ночном спаде трафика молчит весь инцидент, а на утреннем пике будит дежурного на ровном месте. Разберемся, почему Burn Rate — это в первую очередь безразмерный коэффициент, и как из него собирается алерт, который срабатывает по делу.

Начнем с математики#

Сейчас мы посчитаем два числа: сам бюджет ошибок и интенсивность, с которой его можно тратить. Ни одно из них не будет Burn Rate — и вот это как раз самое интересное.

Представим сервис со стабильной нагрузкой в 1 000 запросов в минуту (req/min).

Мы установили SLO = 99.9%.

  1. Определяем бюджет ошибок: \(\text{Error Budget} = 1 - \text{SLO} = 0.1\%\) (или \(0.001\) в долях).
  2. Считаем бюджет в абсолютных величинах:
    • За 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 ошибок равномерно по всему месяцу. Чтобы понять «нормальную» интенсивность ошибок, разделим бюджет на время:

$$\frac{43\,200 \text{ ошибок}}{43\,200 \text{ минут}} = 1 \text{ ошибка в минуту}$$

Это — Absolute Rate (абсолютный показатель). У него есть размерность (ошибки/мин), и он намертво привязан к текущему трафику: на ночном спаде в 100 req/min та же одна ошибка в минуту означает уже десятикратное превышение нормы. Полезное число, но это всё еще не Burn Rate.

Формула коэффициента#

Burn Rate считается не через абсолютные значения, а через доли:

$$\text{Burn Rate} = \frac{\text{Фактическая доля ошибок (за интервал)}}{\text{Допустимая доля ошибок (по SLO)}}$$

Возьмем наш сервис с SLO 99.9% (допустимо 0.1% ошибок). Если за последние 5 минут мы поймали 2% ошибок:

$$\text{Burn Rate} = \frac{0.02}{0.001} = 20\times$$

Обрати внимание на то, что произошло с единицами измерения. И в числителе, и в знаменателе стоит одна и та же величина — доля неуспешных запросов от общего числа. При делении они сокращаются, и на выходе остается голое число. Не «двадцать чего-то в час», а просто двадцать раз.

Два понятия, которые часто путают#

Теперь, когда оба числа посчитаны, разница видна в упор.

Характеристика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-дневного окна. Значит, коэффициент равен доле потраченного бюджета, деленной на долю прошедшего времени:

$$\text{Burn Rate} = \dfrac{0.02}{\dfrac{1}{720}} = 0.02 \times 720 = 14.4$$

Обратная проверка сходится: при постоянном коэффициенте \(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.