{ "index": 152, "slug": "editorial-2023-10-mechanism-sli-slo", "title": "Error budget без магии: как связать SLI, окно и решение", "excerpt": "Error budget появляется не из красного процента, а из явных границ измерения. Разбираем SLI, SLO, знаменатель, окно и policy на воспроизводимом примере и показываем, почему арифметика не является release-gate.", "contentHtml": "

На панели появляется красный процент. В релизном чате говорят: «бюджет почти закончился». Но никто не может быстро ответить, какой пользовательский путь измеряет график, какие события попали в знаменатель и когда началось окно. Один отчёт считает отменённый запрос, другой исключает его. Один смотрит последние 28 дней, другой — календарный месяц.

\n

Цена ошибки — не спор о терминах. Команда может остановить полезное исправление из-за неверной формулы. Или продолжить рискованный rollout, потому что неуспешные события не попали в расчёт. Красный цвет не становится решением, пока за ним нет проверяемого договора.

\n

Пять частей одной модели

\n

SLI — это количественная мера конкретного свойства сервиса. SLO задаёт для этой меры цель или диапазон. Error budget — допустимая часть неуспешных событий внутри той же границы и того же окна. Если команда меняет scope, eligible-события, good-события, окно или target, она меняет модель. Старый процент больше нельзя сравнивать с новым без оговорки.

\n

Начинайте не с девяток. Сначала назовите действие пользователя. Для условного checkout это может быть «пользователь отправил заказ и получил подтверждение». Затем определите множество eligible-событий, правило успеха, исключения, окно, источник данных и владельца. Только после этого формула получает смысл.

\n
Минимальный договор для SLI
ПолеПримерПроверка
Scopecheckout-submitСобытие относится к нужному пользовательскому пути
Eligibleзавершённая попытка отправкиЗнаменатель включает все случаи этой границы
Goodподтверждение получено в срокПравило не зависит от цвета dashboard
Окноrolling 28 daysПериод одинаков в расчёте и обсуждении
Target99,5%Число связано с policy и владельцем
\n

Как работает арифметика

\n

Учебный пример ниже не читает monitoring и не описывает production. В окне есть 1 000 eligible-событий. Из них 994 соответствуют правилу good. SLI равен 994 / 1 000 = 99,4%. При target 99,5% условный budget исчерпан: допустимая доля bad равна 0,5%, то есть пять событий, а фактическая — шесть.

\n
const eligible = 1000; const good = 994; const target = 0.995; const sli = good / eligible; const allowedBad = eligible * (1 - target); const actualBad = eligible - good; console.log({ sli, allowedBad, actualBad, budgetExhausted: actualBad > allowedBad }); // учебный результат: { sli: 0.994, allowedBad: 5, actualBad: 6, budgetExhausted: true }
\n

Числа в коде намеренно synthetic. Они не доказывают доступность, burn rate, incident или стоимость простоя. В рабочей системе нужно подтвердить, откуда пришло каждое событие и почему оно относится к scope. Формула без этого лишь аккуратно делит неизвестные данные.

\n

Арифметика должна иметь отрицательные ветки

\n

Учебный расчёт полезен только вместе с проверками входа. Нулевой знаменатель нельзя превращать в 100%: это состояние no-data, а не доказательство успеха. Значение good, превышающее eligible, указывает на ошибку агрегации или источника. Target вне диапазона от нуля до единицы нельзя молча принимать как SLO. Такие условия лучше отвергать до построения графика.

\n
node --input-type=module <<'EOF'\nfunction check({ eligible, good, target, windowDays }) {\n  if (!Number.isInteger(eligible) || eligible <= 0) throw new Error('eligible > 0');\n  if (!Number.isInteger(good) || good < 0 || good > eligible) throw new Error('0 <= good <= eligible');\n  if (!(target > 0 && target < 1)) throw new Error('0 < target < 1');\n  if (windowDays !== 28) throw new Error('window is 28 days in this fixture');\n\n  const bad = eligible - good;\n  const sli = good / eligible;\n  const exactAllowedBad = eligible * (1 - target);\n  return {\n    sli,\n    bad,\n    allowedBad: Number(exactAllowedBad.toFixed(6)),\n    budgetExhausted: bad > exactAllowedBad,\n    releaseAuthority: 'not-granted'\n  };\n}\n\nconsole.log(check({ eligible: 1000, good: 994, target: 0.995, windowDays: 28 }));\nEOF\n\n# ожидается: sli 0.994, bad 6, allowedBad 5, budgetExhausted true\n
\n

Запуск не обращается к monitoring, CI, сети, HTTP, SDK или часам. Поле releaseAuthority намеренно не даёт fixture полномочий: PASS подтверждает арифметику и границы входа, но не доступность сервиса и не разрешение на rollout.

\n

Есть и отрицательный путь. Если знаменатель равен нулю, процент нельзя объявлять равным 100%. Если good больше eligible, источник или преобразование сломаны. Если сервис измеряет только HTTP-ответ, а пользовательская ценность появляется после фоновой обработки, SLI может быть полезным proxy, но не прямым измерением результата. Proxy gap надо назвать явно.

\n

Окно не лечит плохой знаменатель

\n

Rolling window показывает недавнее состояние и постепенно вытесняет старые события. Fixed window проще связать с отчётным периодом. Ни один режим не исправляет ошибку в eligible set. При малом трафике одна ошибка резко меняет процент. При большом трафике среднее может скрыть хвост задержки. Для latency среднее также может быть слишком грубым: несколько очень медленных запросов исчезнут в общей цифре.

\n

Окно нужно записать рядом с формулой, а не оставить подписью графика. При смене 28 дней на 30 дней, при смене fixed на rolling или при изменении часового пояса меняется сравнение. Пересчитайте исторические значения либо пометьте границу новой версией договора.

\n

Симптом → причина → проверка → действие

\n
Диагностическая матрица для SLI/SLO
СимптомПричинаПроверкаДействие
Два отчёта показывают разные SLIРазные scope или exclusionsСравнить определения eligible и good на трёх одинаковых событияхВерсионировать один контракт и убрать скрытый фильтр
Процент равен 100% при отсутствии трафикаНулевой знаменатель превращён в успехПроверить обработку пустого окнаВернуть состояние no-data и отдельное правило для него
Красный budget не связан с жалобамиSLI измеряет proxy или не тот путьСопоставить событие метрики с user journeyСузить scope либо добавить пользовательский сигнал
После смены окна исчезла деградацияСравнили несовместимые периодыПроверить версию окна и границы timestampПересчитать историю или явно разделить серии
Budget требует немедленной блокировкиНет policy и проверки контекстаНазвать owner, обратимость и тип измененияВыбрать review, rollback, сужение rollout или продолжение с контролем
\n
Связь SLI-контракта, окна и error budget: scope задаёт eligible-события, good-события формируют SLI, target задаёт допустимый budget, а policy определяет действие.
Механизм начинается с границы события. Процент появляется после определения scope, eligible, good, окна и target. Policy связывает результат с ручным решением; сама арифметика не выдаёт право блокировать релиз.
\n

Проверка знаменателя на трёх событиях

\n

Перед тем как обсуждать процент, возьмите один good, один bad и один спорный случай. Для каждого ответьте на четыре вопроса: относится ли событие к scope, почему оно eligible, какое поле доказывает good и когда событие попало в окно. Эта маленькая выборка обнаруживает скрытый фильтр быстрее, чем ещё один dashboard.

\n
Что делать при расхождении модели
НаблюдениеПроверяемРешение до новых данных
Два запроса дают разный знаменательScope, exclusions и версию фильтраПриостановить сравнение и оставить одну формулу
Процент равен 100% без событийВетку no-dataОтделить отсутствие наблюдений от успеха
Backend good, пользователь видит сбойУчасток пути после серверного ответаНазвать SLI proxy и добавить клиентский сигнал
\n

Budget не является автоматическим gate

\n

Расход бюджета — сигнал для принятия решения, а не универсальная команда остановить deploy. Policy должна назвать владельца, обязательные evidence, допустимые действия и исключения. Security fix может потребовать другого пути согласования. Обратимый rollout может потребовать сужения exposure. Неверный расчёт требует остановить интерпретацию метрики, а не обязательно остановить весь релиз.

\n

Отдельно разделяйте SLI и диагностику. SLI отвечает на вопрос, нарушается ли выбранная мера. Логи, трассы, версии, очереди и зависимости помогают искать причину. Один сигнал не обязан объяснять другой. Если после красного процента команда сразу объявляет incident, она пропускает проверку scope, времени и источника данных.

\n

Порядок проверки

\n
  1. Зафиксируйте симптом. Сохраните значение, timestamp, версию SLI-контракта и точный user journey. Не меняйте формулу во время расследования.
  2. Проверьте границу. Для одного good, одного bad и одного спорного события определите, попадает ли каждое в eligible и почему.
  3. Пересчитайте малую выборку. Сравните ручной подсчёт с запросом или exporter. Отдельно проверьте нулевой знаменатель и ошибочное превышение good над eligible.
  4. Проверьте окно. Сверьте начало, конец, timezone, fixed или rolling режим и задержку доставки событий.
  5. Отделите proxy от результата. Проверьте, измеряет ли событие ценность пользователя или только слой системы. Запишите расхождение.
  6. Примените policy. Назначьте owner и выберите обратимое действие: исправить источник, сузить rollout, отложить non-urgent изменение или продолжить с контролем.
  7. Повторите расчёт. Используйте ту же формулу и тот же критерий. Если сигнал не изменился ожидаемым образом, вернитесь к гипотезе.
\n

Ограничения

\n

SLI не измеряет всё качество продукта. Доступность backend не гарантирует успешный пользовательский сценарий. Error budget не показывает корневую причину и не определяет важность изменения. Target 99,5% и окно 28 дней в примере не являются рекомендацией. Для редкого трафика, пакетной обработки, долгих операций и юридического SLA нужны отдельные решения.

\n

Учебный код также не создаёт alert, не читает реальные события, не меняет CI и не принимает решение о выпуске. Его можно использовать для проверки арифметики и отрицательных веток. Production-вывод появляется только после проверки источника данных, владельца, разрешений и поведения системы на реальном трафике.

\n

Критерий готовности

\n

Договор готов, если инженер за несколько минут может показать scope, eligible, good, exclusions, окно, target, источник, owner и policy branch. Для трёх выбранных событий два инженера получают одинаковый ответ. Пустое окно не становится успешным автоматически. После действия та же версия формулы даёт ожидаемое изменение, а новое значение можно связать с timestamp и источником.

\n

Если хотя бы одно поле неизвестно, статус должен быть «договор не готов». Не добавляйте ещё один график поверх неопределённости. Сначала восстановите границу измерения, затем решайте, какое действие безопасно.

\n

Проверяемые источники

\n" }