Постмортем «Чёрной пятницы»: 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). Частично готовый индекс отвечал на запросы с нулевыми хитами — но отвечал.

Поиск вернул «ничего не найдено». Фронт показал пустую категорию. Пользователь ушёл.

Почему стандартный мониторинг это пропустил


Все инструменты, которые обычно настроены в продакшене, смотрят на три вещи:

  1. Ресурсы сервера (CPU, RAM, диск). Пустой ответ ресурсы не ест.
  2. HTTP-коды (4xx, 5xx). Наш ответ — 200.
  3. Латентность (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 минут.

  1. req_id — уникальный идентификатор запроса. Проходит через все сервисы (фронт → API → БД → внешние). Позволяет «прошить» любой запрос поперёк логов.

  2. endpoint — логический идентификатор, не URL. /api/catalog/filter, не /api/catalog/filter?cat=12&price=100-500. Параметры хранятся отдельно. Иначе метрики размазываются на тысячи «уникальных» URL.

  3. status + bytes — и HTTP-статус, и размер ответа. HTTP 200 с bytes < 200 по эндпоинту, который обычно возвращает 10 KB — это сигнал.

  4. latency — в миллисекундах, с разбиением p50/p95/p99. Среднее бесполезно (скрывает хвосты).

  5. 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

Расскажите нам о своем проекте

Наши офисы

  • Россия
    Россия, Санкт-Петербург, Рижская улица, 5, корп. 1 офис 402
    +7 (967) 555-90-32
  • Казахстан
    Алма-Ата
    +7 (707) 340-29-12