Один алерт на p99, пять медленных маршрутов, пять разных причин
Алерт на латентность уровня сервиса не называл ни одного эндпоинта. Разбивка по маршрутам показала внешний шлюз, проверку капчи, вызов провайдера — и набор ловушек измерения, которые увели бы нас не туда.
На одной продуктовой платформе в Gambchamp сработал алерт на p99 латентности платёжного сервиса. Он говорил, что сервис медленный, и ничего — о том, где именно. Разбивка алерта по маршрутам ответила на этот вопрос — и показала, что у каждого медленного маршрута своя причина. Какие-то были реальными проблемами, какие-то — совсем не тем, чем казались.
Чем оказался каждый маршрут
- Инициация депозита: примерно половина запросов занимала 2,5–5 секунд. Это были синхронные вызовы внешних платёжных шлюзов, возвращавшие HTTP 200 с URL для редиректа, — никаких ошибок. Распределение было бимодальным: около 5 мс, когда вызов шлюза не нужен, и 2,5–5 с, когда нужен. p99 в восемь секунд — просто нормальная скорость шлюзов.
- Регистрация: 84 % запросов дольше секунды. Тайминги фаз в логах показали, что медианный запрос тратит 1,4 с на обработку, большую часть — на внешнюю проверку капчи (около 0,5 с).
- Запуск игры: 6 % медленных. Одна фаза — вызов коннектора игрового провайдера — имела медиану 1,2 с и p95 1,7 с. Всё остальное — миллисекунды.
- Промо-страница: 10 % запросов дольше секунды — но собственный тайминг обработчика ни разу не превысил 0,95 с. Время уходило за пределами обработчика. Эту до конца пока не поймали.
- Вывод средств: ещё один маршрут через шлюз, p99 около 2,5 с. Все остальные платёжные маршруты — около 50 мс.
Ловушки
- Бэкенды скрейпились дважды, двумя job’ами. Квантилям это безразлично, но счётчики и
increase()удваиваются, если не зафиксировать job. Всегда фиксируйте его, когда считаете запросы. - Тайминг обработчика в логах — это не длительность запроса. Он меряет только обработчик; гистограмма меряет то, что видит клиент. Для SLO доверяйте гистограмме; логи используйте, чтобы понять, куда уходит время внутри обработчика.
- Логи — это выборка. 763 строки лога против 2 419 наблюдений гистограммы за те же три часа. «В логах медленных запросов нет» не опровергает метрику.
- Сетка бакетов прыгала с 1,0 сразу на 2,5 секунды. Всё, что попадает в этот промежуток, интерполируется ближе к верху, поэтому p99 в 2,3 с может означать «где-то между 1 и 2,5». Смотрите на бакеты, прежде чем доверять числу.
- Квантиль прячет бимодальность. «p99 — пять секунд» звучало как «всё медленное». Бакеты показали два разных пути в коде.
Одну ошибку я сделал сам: сначала списал промо-страницу на артефакт бакетов. Бакеты разных job’ов совпали друг с другом — примерно 10 % запросов действительно были дольше секунды. Проверяйте, прежде чем объявлять артефакт.
Запросы, которые сделали работу
sum by (le) (increase(http_request_duration_seconds_bucket{app="X", route="Y"}[30m]))
Полное распределение показывает бимодальность и где сидят секунды. Доля запросов дольше секунды по маршрутам показывает, у каких маршрутов реальный хвост. А CPU throttling стоит исключить, прежде чем винить приложение.
Как изменился алертинг
- Алерт на латентность теперь агрегирует по
(app, route), так что называет эндпоинт. - Порог по трафику — больше 3 запросов за 10 минут, а не rate: маршрут депозита получает около четырёх запросов за 10 минут и действительно медленный, так что порог по rate его пропускал.
- Порог — 1,5 с, а не 1 с, выбран с оглядкой на сетку бакетов.
- Маршруты через шлюзы исключены и получили собственный алерт на 12 секунд за 30 минут. Две–восемь секунд там — норма; текст алерта прямо это говорит, чтобы дежурный не бросился искать баг в нашей инфраструктуре.
Выводы
- Алерт на латентность уровня сервиса должен называть маршрут.
- Смотрите на распределение, а не только на квантиль.
- Фиксируйте job скрейпа, когда что-то считаете.
- Внешние зависимости получают свои пороги и явную пометку в алерте.