{ "index": 153, "slug": "editorial-2023-10-practice-sli-slo", "title": "SLI и SLO: как измерять пользовательский результат, а не цвет графика", "excerpt": "Процент успешных ответов не становится SLI сам по себе. Разбираем путь пользователя, знаменатель, хороший исход, окно и цель; показываем учебный расчёт, отрицательный путь и критерий готового договора.", "contentHtml": "
На дашборде сервис зелёный: 99,9% запросов завершились без HTTP 5xx. Пользователь всё равно нажимает «Оплатить» второй раз. Первый запрос принял API, но очередь не создала платёж, а клиент получил тайм-аут после точки измерения. Команда видит хороший процент и плохой результат. Цена ошибки — неверный приоритет: релиз считают безопасным, расследуют не тот компонент и позже спорят, был ли сбой частью SLO.
\nSLI и SLO исправляют эту ошибку только при точном договоре. SLI отвечает на вопрос «что измеряем?». SLO задаёт цель для этого измерения. Договор должен назвать путь пользователя, события в знаменателе, хороший исход, исключения, окно, источник данных и владельца решения. Если хотя бы одно поле скрыто, процент остаётся удобной, но декоративной метрикой.
\nGoogle SRE определяет SLI как количественную меру свойства сервиса, а SLO — как целевое значение или диапазон для этой меры. Из этого следует практический порядок: сначала назвать важное для пользователя действие, затем выбрать измеримый признак. Не начинайте с готового счётчика HTTP-кодов только потому, что он уже есть в системе.
\nДля checkout полезный вопрос звучит так: «Как часто завершённая попытка оформления получает подтверждение, которое клиент может показать пользователю?» Это ещё не SLI. Он задаёт границу. Теперь нужно решить, какое событие означает завершённую попытку и какое событие означает успех. Ответы должны быть наблюдаемыми и одинаково понятными владельцу продукта, разработчику и on-call.
\nУptime процесса может остаться диагностическим сигналом. Он показывает состояние компонента, но не доказывает успех пользовательского маршрута. И наоборот, один backend-ответ может быть плохим, а продукт — успешно показать fallback. Поэтому название proxy должно оставаться явным. Proxy нельзя выдавать за прямое измерение результата.
\nДля success ratio удобно разделить события на eligible и good. Eligible — все попытки, которые имеют право попасть в знаменатель. Good — подмножество eligible с заранее названным допустимым исходом. Тогда показатель считают так: SLI = good / eligible. SLO задаёт нижнюю границу, например 99% в выбранном окне. Error budget равен допустимой доле bad-событий в том же знаменателе и окне.
Правило исключения нужно записывать рядом с формулой. Отмена клиентом до отправки запроса может не входить в eligible. Отмена после принятия запроса может быть уже частью результата, если сервис обязан её обработать. Нельзя молча вычёркивать спорное событие после того, как оно ухудшило график. Иначе два отчёта получат разные знаменатели, хотя ссылаются на один маршрут.
\n| Часть | Значение | Проверка | Ограничение |
|---|---|---|---|
| Путь | Отправка оформленного checkout | Есть идентификатор попытки и граница завершения | Не описывает весь сайт |
| Eligible | Запрос дошёл до точки завершения API | Событие содержит request_id и итоговый статус | Не включает отмену до запроса |
| Good | Клиент получил подтверждение приёма платежа | Статус связан с пользовательским ответом, а не только с 2xx | Не доказывает фактическое списание |
| Окно | 28 дней в учебном примере | Все сравнения используют одну границу времени | Не является универсальным окном |
| Цель и владелец | 99%; владелец checkout | Есть правило пересмотра и способ связи | Не даёт автоматического права блокировать релиз |
Число 99% без окна и знаменателя неполно. Сто успешных попыток из ста дают 100%, но один сбой при десяти попытках меняет показатель сильнее, чем один сбой при миллионе. Для малотрафикового маршрута процент может быть шумным. Для пакетной обработки важнее throughput или время завершения. Окно выбирают вместе с типом нагрузки и решением, которое SLO должно поддержать.
\nНиже — ограниченный пример. Он проверяет только арифметику договора на заранее заданных числах. Он не читает логи, не обращается к мониторингу и не сообщает состояние какого-либо сервиса. В учебном окне есть 1 000 eligible-событий, из них 994 good. Показатель равен 99,4%. При SLO 99% допустимы 10 bad-событий, а в примере их 6. Остаток условного бюджета — 4 события.
\nconst eligible = 1000;\nconst good = 994;\nconst target = 0.99;\n\nconst bad = eligible - good;\nconst sli = good / eligible;\nconst allowedBad = eligible * (1 - target);\nconst budgetLeft = Math.max(0, allowedBad - bad);\n\nconsole.log({\n sliPercent: sli * 100,\n bad,\n allowedBad,\n budgetLeft,\n});\n// Учебный вывод: 99.4%, 6, 10, 4\n// Числа не являются измерением production-сервиса.\nФормула полезна только при сохранении условий. Если из знаменателя убрать неудачные попытки, показатель вырастет без улучшения пути. Если заменить good на «ответ не 5xx», можно начать считать принятый запрос успехом, хотя пользователь ещё не получил подтверждение. Если смешать 28-дневное окно с дневным числом ошибок, error budget потеряет смысл.
\nЦель 100% в таком примере не нужна: она скрывает допустимый риск и превращает каждое событие в повод для ручного спора. Это не запрет на строгие требования. Для финансовой операции продукт может выбрать особое правило, но оно должно быть обосновано сценарием, риском и способом проверки. Учебный target не переносится в рабочую систему автоматически.
\nПоследний шаг важен. SLO не объясняет корневую причину. Для неё нужны диагностические сигналы: логи, метрики, трассы или данные продукта. OpenTelemetry описывает эти сигналы как разные виды наблюдений: trace показывает путь запроса, metric — измерение во времени, log — запись события. Их можно связать контекстом, но ни один сигнал не доказывает причину без проверки источника и границы времени.
\nЕсли событие не содержит request_id, нельзя надёжно связать его с пользовательской попыткой. Если good event появляется раньше фактического завершения, SLI измеряет промежуточный шаг. Если трафика мало, процент может не поддерживать срочное решение. Если внешний провайдер недоступен, нужно заранее определить, входит ли его сбой в договор и кто владеет реакцией. Если событие потеряно, отсутствие строки нельзя считать успехом.
\nОтрицательный путь должен быть виден в примерах: нет знаменателя; good больше eligible; в событии отсутствует поле, необходимое для границы; отмена произошла после отправки и ошибочно исключена; два источника считают разные окна. В каждом случае честное действие — остановить интерпретацию и исправить договор или источник. Нельзя дорисовать процент, чтобы сохранить зелёный статус.
\nУчебная арифметика также не доказывает SLO compliance, availability, burn rate, incident или безопасность релиза. Она не показывает реальную стоимость простоя. Официальные документы Google помогают выбрать термины и форму описания, но не назначают target конкретному продукту. Производственный критерий должен опираться на реальные события, согласованный владелец и проверяемое правило реакции.
\nДоговор готов, если независимый инженер может без устных пояснений ответить на семь вопросов: какой путь измеряем; что входит в eligible; что считается good; какие исключения действуют; за какое окно считается показатель; где лежат исходные события; кто принимает решение при отклонении. Для трёх заранее выбранных событий формула должна дать одинаковый результат в отчёте и в проверочном запросе. После изменения должен существовать обратимый шаг и способ повторить ту же проверку.
\nЕсли на любой вопрос нет ответа, готовность не достигнута. Сначала исправьте границу и названия событий. Потом меняйте дашборд, алерт или policy. Такой порядок защищает от главной ошибки SLI/SLO: принять число, которое легко измерить, за результат, который действительно важен пользователю.
\n