Модуль 8. «Чёрная пятница»: несколько отказов одновременно

Цель модуля: пройти комбинированный инцидент — когда одновременно деградируют база, реплика и внешняя зависимость — и научиться действовать по регламенту: зафиксировать влияние, локализовать слои, приоритизировать, восстановить и написать постмортем без поиска виноватых.

Перед началом: модули 1, 2 и 7 (индекс и пул, лаг реплики, отказ партнёра). Инструменты: стенд polygon/, Grafana, psql, make. Трудоёмкость: ~2–3 ч (инцидент 40 мин · разбор 40 мин · постмортем 40 мин).

🔥 Инцидент: «всё сломалось одновременно»

Распродажа, 12:00. За первые пять минут приходят три сигнала:

  1. p99 листинга заказов вырос с 0.2 с до ~4 с, в логах api — таймауты чтения.
  2. Поддержка: «клиент оплатил, а заказ не появился в списке».
  3. p99 вызовов партнёра (проверка риска) — секунды, часть запросов висит.

Выкаток не было. В базе — уборка индексов, на реплике — «плановое обслуживание» с паузой replay, у партнёра — деградация на их стороне. По отдельности каждый отказ знаком по модулям 1, 2 и 7; вместе они дают картину «лёг весь сервис», в которой легко запаниковать и начать чинить не то.

Задача модуля — не выучить новый паттерн, а отработать порядок действий: сначала влияние и масштаб, потом слои, потом причины. Один и тот же набор метрик и SQL-запросов, что в предыдущих модулях, только под давлением.

📖 8.1. Первые пять минут: влияние, а не причины

Порядок, который экономит часы:

  1. Что не работает для пользователя. Листинг? Создание? Оплата? Один маршрут или все? Ответ — в Grafana (латентность и RPS по маршрутам) и в /healthz против /readyz.
  2. Когда началось. Сопоставьте рост p99 с временем изменений: деплой, миграция, обслуживание, рост трафика.
  3. Что изменилось. Дежурный не обязан знать про ночную уборку индексов; он обязан спросить «что меняли за последние сутки» и посмотреть pg_stat_activity, лаг реплики, алерты.
  4. Масштаб. Один пользователь, один маршрут, весь сервис, вся система? Это определяет, тушить ли точечно или объявлять общий инцидент.

В комбинированном сценарии все три отказа видны на разных панелях: латентность /orders, лаг реплики (SQL), p99 /risk и исходы вызовов партнёра. Панели не складываются в одну причину — и это нормально: причин три.

Цикл разбора инцидента: симптом → слой → причина → проверка → защита

Схема — ваш маршрут в любом инциденте: подтвердить симптом цифрой, определить слой, проверить гипотезу инструментом, починить причину и вернуться по кругу, пока make check-N не станет зелёным. Постмортем — не финальная галочка, а следующий вход в цикл: найденная защита должна закрыть повторение.

📖 8.2. Приоритизация: что чинить первым

Правило простое: сначала то, что возвращает пользователю основной сценарий, и только потом то, что уменьшает шум.

ОтказВлияниеЧто делать первым
Нет индекса на /ordersосновной сценарий, все пользователивосстановить индекс
Пауза replay на репликечасть пользователей не видит свои заказывозобновить replay
Партнёр виситвторичная проверка, деградациявключить защиты, не тушить партнёра

Индекс и replay — причины, которые вы контролируете; партнёр — внешняя зависимость, её «чинят» защитами (bulkhead, circuit breaker, таймаут), а не перезапуском чужого сервиса. Если начать с партнёра, листинг всё равно останется медленным, и пользователи не увидят улучшения.

📖 8.3. Чек-лист дежурного

Держите его перед глазами во время инцидента:

  • Подтвердить симптом цифрой (p99, доля ошибок, лаг) и время начала.
  • Проверить /healthz и /readyz: жив ли процесс, готовы ли зависимости.
  • Посмотреть Grafana: RPS, p99 по маршрутам, 5xx, пул, кэш, партнёр.
  • Проверить БД: pg_stat_activity, блокировки, план подозрительного запроса.
  • Проверить реплику: pg_is_in_recovery(), pg_last_wal_replay_lsn(), pg_wal_lsn_diff(), состояние replay.
  • Проверить пайплайн (если full): лаг группы sink, лаг слота, /stats.
  • Проверить зависимости: /debug/partner, исходы вызовов.
  • Записать таймлайн действий — он понадобится для постмортема.

📖 8.4. Восстановление и проверка

Сценарий 8 собран из уже знакомых поломок:

bash
make fail-8     # индекс + пауза replay + партнёр без защит
make check-8    # красный: каждый слой проверяется отдельно
make fix-8      # индекс, replay, защиты
make check-8    # зелёный: SLO вернулись

check-8 последовательно запускает check-1, check-2, check-7, а в full-профиле ещё check-3 и check-4. Это важно: комбинированный инцидент считается закрытым только когда зелёные все слои, а не тот, который вы чинили последним.

Если что-то пошло не так и стенд в неопределённом состоянии — make reset удаляет тома и возвращает исходные данные; это тоже часть регламента: иногда быстрее пересоздать окружение, чем разбираться с накопленным мусором.

📖 8.5. Постмортем без виноватых

Хороший постмортем отвечает на вопросы «что произошло» и «что изменим», а не «кто виноват». Шаблон на одну страницу:

text
Инцидент: <короткое имя>            Дата/время: <начало — конец>
Влияние: <кто и что не мог делать, сколько пользователей/заказов>
Симптомы: <метрики и цифры на момент обнаружения>
Таймлайн: <время — действие — результат> (5–10 строк)
Причины: <цепочка, а не одна точка; отметьте усилители>
Почему не заметили раньше: <алерты, пороги, слепые зоны>
Что починили: <конкретные действия и проверки>
Защита: <что добавляем, чтобы не повторилось: тест, алерт, лимит, регламент>
Открытые вопросы: <что осталось неизвестным>

В нашем сценарии причины три, и это ключевой вывод: не ищите одну кнопку. Уборка индексов без проверки статистики на реплике, обслуживание с паузой replay без уведомления дежурного и отсутствие защит вокруг партнёра — три независимых решения, которые совпали по времени.

📖 8.6. Компромиссы

  • Тушить всё сразу vs по приоритету. Распараллеливание помогает, но повышает риск ошибочных действий; приоритет — основной сценарий.
  • Быстрое восстановление vs поиск корня. Сначала вернуть SLO (индекс, replay), потом разбираться, почему индекс удалили.
  • Больше алертов vs меньше шума. Алерт на каждый симптом приводит к усталости; лучше несколько алертов на пользовательские SLO плюс дашборд.
  • Blameless vs ответственность. Постмортем без виноватых не отменяет ответственности за решения — он отделяет людей от системных причин.

💻 Практика

  1. Подготовка (10 мин). make up-full && make seed && make debezium. Откройте Grafana и убедитесь, что базовые панели зелёные.
  2. Зафиксируйте норму (5 мин). make load-reads, make load-risk по 10–15 с; запишите p99 /orders и /risk, лаг реплики (SQL), лаг группы payments.
  3. Инцидент (15 мин). make fail-8. Не смотрите скрипты — работайте как дежурный: Grafana → /readyz → pg_stat_activity → реплика → партнёр. Записывайте таймлайн: время, что проверили, что увидели.
  4. Проверка «сломано» (10 мин). make check-8 — красный. Разберите, на каком слое он остановился и почему.
  5. Восстановление (10 мин). make fix-8, затем make check-8 — зелёный. Если check-3/check-4 не проходят, проверьте, что вы в full-профиле и коннектор Debezium в состоянии RUNNING (make debezium).
  6. Постмортем (20 мин). Напишите документ по шаблону 8.5: влияние, три причины, почему не заметили, защита. Один абзац — что бы вы сделали иначе, если бы инцидент повторился завтра.

🧩 Quiz "Проверь себя"

1. Какой результат считается восстановлением в полигоне?
2. Что означает «постмортем без виноватых»?
3. Что чинить первым, если одновременно сломан индекс, реплика и партнёр?
4. Почему check-8 прогоняет проверки всех слоёв, а не только последнего фикса?
5. С чего начинается разбор комбинированного инцидента?

✅ Итоги модуля

  • Пройти комбинированный инцидент fail-8 → check-8 (red) → fix-8 → check-8 (green).
  • Действовать по чек-листу дежурного, а не по памяти.
  • Объяснить приоритизацию: основной сценарий раньше вторичных зависимостей.
  • Написать постмортем по шаблону: влияние, таймлайн, три причины, защита.
  • Назвать по одной защите для каждого из трёх отказов.
  • Уметь вернуть стенд в исходное состояние через make reset.

🎓 Финал: от стенда к проду

Полигон закончился, но регламент остался: подтвердить симптом цифрой, локализовать слой, проверить гипотезу инструментом, починить причину, оставить защиту. Эти пять шагов работают и на локальном compose, и на проде с Kubernetes — меняются только инструменты.

Что стоит сделать дальше на своём проекте:

  • перенести три алерта из курса на реальные SLO (латентность, ошибки, лаг);
  • добавить проверку статистики индексов на той реплике, куда идут чтения;
  • завести регламент обслуживания реплик с уведомлением дежурного;
  • обернуть каждую внешнюю зависимость таймаутом, bulkhead и circuit breaker;
  • научиться писать постмортем на одну страницу — и писать его после каждого инцидента, даже маленького.