Постмортем «Чёрной пятницы»: 14 минут, 600 000 ₽ и пять полей в логе, которых у вас нет
Автор: WebGoodPeople
Пятница, 18:03. Каталог клиента перестал отдавать данные. Сервер на дашборде — зелёный.
Это реальный инцидент из наших проектов. Детали обобщены — компанию не называю. Но механика повторяется в каждом пятом e-com, с которым мы работаем. Если у вас Битрикс или любой монолит с надстроенным поиском — почти наверняка этот паттерн есть и у вас. Разбираю его так, как мы разбираем его сами, чтобы вы могли проверить себя до того, как он выстрелит.
Таймлайн
- 18:03. Пользователь кликает на фильтр в категорийке. Страница грузится 912 мс, возвращается пустой каталог. Никаких ошибок.
- 18:07. Второй пользователь. Третий. Пятый. Все видят «По вашему запросу ничего не найдено».
- 18:11. Оперативный менеджер пишет нам в чат: «Сайт работает, но корзина пустая. Продаж нет».
- 18:17. Мы находим проблему в логах API.
Четырнадцать минут. Не час. Не сутки. Но при трафике «Чёрной пятницы» это примерно 600 000 ₽ недополученной выручки — консервативная оценка по модели 500k сессий/мес, 1.2% конверсии, среднего чека 4500 ₽, растянутой на пиковое окно.
Что показывал дашборд сервера
Зелёные графики:
- CPU — 34%
- RAM — 61%
- Disk I/O — в норме
- 5xx ошибки — 0
- Время ответа 200 OK — 900 мс (немного дольше обычного, но в пределах)
Никаких алертов. Никаких триггеров. Классический «сайт работает».
Что показывал лог API
2026-XX-XX 18:03:14 req=a3f1 /api/catalog/filter 200 bytes=127 latency=912ms index_version=v17-rebuild 2026-XX-XX 18:03:14 req=b4e2 /api/catalog/filter 200 bytes=127 latency=847ms index_version=v17-rebuild
Две детали, которые изменили всё:
bytes=127. Это размер ответа. 127 байт — это JSON {"items":[],"total":0,"took":...}. Пустая выдача. HTTP 200, но контента нет. Стандартные мониторинги этого не ловят, потому что статус-код корректный.
index_version=v17-rebuild. Это наше поле. Оно говорит, что запрос попал в индекс Elasticsearch, который в этот момент перестраивался (плановая переиндексация каталога после импорта из 1C). Частично готовый индекс отвечал на запросы с нулевыми хитами — но отвечал.
Поиск вернул «ничего не найдено». Фронт показал пустую категорию. Пользователь ушёл.
Почему стандартный мониторинг это пропустил
Все инструменты, которые обычно настроены в продакшене, смотрят на три вещи:
- Ресурсы сервера (CPU, RAM, диск). Пустой ответ ресурсы не ест.
- HTTP-коды (4xx, 5xx). Наш ответ — 200.
- Латентность (p95/p99 времени ответа). 912 мс — не катастрофа.
Ни одна из этих метрик не умеет сказать «вы ответили пустотой там, где должен был быть товар». Для этого нужно смотреть не на уровень инфраструктуры, а на уровень бизнес-логики.
Это и есть то, что мы называем green-dashboard blind spot — слепое пятно зелёного дашборда. Когда каждая метрика показывает «всё хорошо», а бизнес-результат — потерян.
Что мы изменили в тот же вечер
1. Алерт на падение p95-количества результатов
Не на отсутствие результатов. На падение по сравнению с бейзлайном.
Для каждого критичного endpoint мы знаем, сколько он возвращает в среднем:
/api/catalog/filter— p95 = 48 товаров/api/search/query— p95 = 12 товаров/api/recommendations— p95 = 6 товаров
Если p95 падает больше чем на 80% от 5-минутного окна — on-call получает пейджер. Не «сайт лёг», а «мы отдаём меньше товара, чем обычно».
Настройка заняла около часа по всем критичным эндпоинтам.
2. Поле data_version в каждом API-ответе
Теперь каждый ответ API несёт в метаданных версию данных, из которых он был сформирован. Для Elasticsearch это index_version, для Redis-кэша — cache_epoch, для материализованных представлений — mv_refreshed_at.
Когда случается аномалия — мы сразу видим, какая версия данных её вернула. Был ли это стейл-кэш? Частично перестроенный индекс? Зависшая MV? Всё это проявляется как один фильтр в Grafana — и сразу становится понятно.
3. Runbook на одну страницу
Runbook — это инструкция «что делать, если сработал алерт». Раньше она была в голове одного разработчика. Теперь — в Notion, на одной странице, с тремя блоками:
- Что случилось (первые 3 проверки: какой алерт, какой endpoint, какая версия данных)
- Как быстро откатить (rollback-кнопка в админке, переключение на snapshot индекса)
- Кого будить дальше, если первые 3 шага не сработали
Runbook пишется для дежурного инженера на 15 минут сна. Не для CTO со свежей головой. Это разные тексты.
5 полей в API-логе, без которых мы не работаем
Спустя этот инцидент мы закрепили минимальный набор полей. Если эти пять полей у вас есть — инциденты типа «зелёный дашборд, пустой каталог» ловятся за 60 секунд, а не 14 минут.
-
req_id— уникальный идентификатор запроса. Проходит через все сервисы (фронт → API → БД → внешние). Позволяет «прошить» любой запрос поперёк логов. -
endpoint— логический идентификатор, не URL./api/catalog/filter, не/api/catalog/filter?cat=12&price=100-500. Параметры хранятся отдельно. Иначе метрики размазываются на тысячи «уникальных» URL. -
status+bytes— и HTTP-статус, и размер ответа. HTTP 200 сbytes < 200по эндпоинту, который обычно возвращает 10 KB — это сигнал. -
latency— в миллисекундах, с разбиением p50/p95/p99. Среднее бесполезно (скрывает хвосты). -
data_version— версия данных, из которых сформирован ответ. Индекс, кэш, снапшот — любая модель данных, которая может быть неконсистентна.
Всё, что за пределами этих пяти — полезно, но не обязательно. Эти пять — must-have.
Что это значит для вас
Если у вас e-com на Битриксе или другом монолите, почти с вероятностью 1 у вас есть одно или несколько из следующих слепых пятен:
- Поиск возвращает пустоту во время переиндексации (и никто не знает)
- Кэш отдаёт стейл-данные после обновления (и никто не знает)
- Один endpoint отвечает с другим распределением латентности, чем остальные (и никто не знает)
- Ошибка фронта «ничего не найдено» не отличается от успешной пустой выдачи (и никто не знает)
Проверить себя просто: откройте логи продакшена. Есть ли в каждой строке req_id, endpoint, status, bytes, latency, data_version? Если нет — у вас те же 14 минут, что у нас.
Фикс стоит 40 минут настройки алертов и 1–2 дня работы на добавление полей в API. Расчёт окупаемости — одна неделя в любом e-com с выручкой от 30 млн ₽/мес.
Если хотите, чтобы мы посмотрели на ваш API-лог и указали слепые пятна — это входит в наш 48-часовой аудит. Бесплатно, без обязательств. webgoodpeople.com/api-audit