Иногда инцидент начинается не с 5xx и не с красного графика

Он начинается с одной стойки

И с одного таймаута

09:12 - alert: db-replica-02 connection timeout

HAProxy зелёный
HTTP 5xx = 0.2%
p50 = 38–42ms

Минимальная схема
Минимальная схема

2 стойки
В каждом свой ToR
Primary и app в rack-1
Replica-01 в rack-1
Replica-02 в rack-2

Read-routing выполняется на уровне приложения (driver read-replica strategy). PgBouncer не занимается распределением между репликами

Health check HAProxy:

option httpchk GET /health

/health проверяет:

  • app process alive

  • connect() к PgBouncer socket

DB round-trip не выполняется:

SELECT 1;

Мониторинг меряет HTTP, latency, replication lag
Отказ начался на уровне стойки

09:12-09:16

Replica-02 (стойка 2) перестаёт отвечать

PostgreSQL:

could not connect to server: Connection timed out
timeout after 300ms

Replication lag на replica-02 начинает быстро расти по мере накопления WAL на primary

Service discovery помечает replica-02 как unhealthy
Primary и replica-01 остаются доступными, write-path не нарушен

Приоритет инцидента не повышается

Потому что HAProxy health check проверяет только /health без DB round-trip → пул остаётся зеленым → инцидент не объявляется

HTTP 200 продолжают возвращаться

Retry-конфиг

connect_timeout = 300ms
statement_timeout = 800ms
retry_attempts = 3
retry_backoff = 200ms
failover = next_replica

Значимая доля read-запросов (около 50% при round-robin) шла на replica-02

Сценарий для такого запроса:

  1. connect → 300ms timeout

  2. backoff 200ms

  3. retry → переключение на replica-01

  4. SELECT выполняется

  5. клиент получает 200

Потому что retry происходит внутри app до формирования HTTP-ответа → финальный статус 200 → 5xx остаётся baseline

Мониторинг считает финальный HTTP статус
Первичная ошибка не отражается отдельной метрикой

09:14 - сеть

OOB (IPMI) replica-02 недоступен

В этом DC management VLAN IPMI также сходится в ToR-2 (общий ToR для data и mgmt в rack-2)

Data-интерфейс:

ToR port Gi1/0/24: up
FDB: MAC present
ARP: incomplete
ICMP: no reply

IPMI и data-plane теряют связность одновременно

Потому что телеметрия на уровне стойки отсутствует и OOB не влияет на приоритет инцидента → одновременная недоступность management и data-plane трактуется как хостовая проблема → эскалация откладывается


09:20 - метрики

p50 ≈ 40ms
p95 ≈ 130ms
p99 = 620-900ms

Почему p99 ≈ 600-900ms при timeout 300ms?

Запросы, попавшие на replica-02:

300ms connect timeout

  • 200ms backoff

  • ~40-60ms успешный SELECT = ~550-600ms

Хвост до 800–900 ms формировался запросами, которые проходили через connect timeout (300 ms) и backoff (200 ms), а затем задерживались при получении соединения из пула. Повторные попытки увеличили время удержания соединений и привели к временному насыщению пула

Потому что reads распределяются между репликами → часть трафика регулярно упирается в timeout → retry увеличивает хвост → p99 растёт

SLA = 1.5s
Формально не нарушен

09:24 - пауза

Primary жив
Replica-01 жива
Replica-02 недоступна

Потеряна межстоечная отказоустойчивость

Приоритет инцидента оценивает текущее влияние, а не потерю отказоустойчивости → пауза становится системной

Система продолжала жить, но отказоустойчивость стала "локальной" внутри rack-1

Отказ rack-1 превратил бы ситуацию в полноценный отказ сервиса


09:30 - коммутатор

На ToR-2:

LINK-3-UPDOWN: Interface Gi1/0/18, changed state to down
LINK-3-UPDOWN: Interface Gi1/0/18, changed state to up

Flapping

Uplink:

LACP renegotiation detected

Ошибки интерфейса:

show interfaces counters errors
CRC: 1487 (growing during instability)

CRC росли в период нестабильности аплинка

Вторая нода в rack-2 начинает терять пакеты

ToR ведёт себя нестабильно, аплинк флапает, хосты rack-2 периодически теряют связность

09:33 - повышение приоритета инцидента

Причины:

  • replica-02 недоступна

  • flapping в rack-2

  • потеря межстоечного резервирования

Инцидент переведён в категорию высокого приоритета
Отправлен on-site инженер


09:41 - ToR reboot-loop

Логи NOS:

kernel panic: process netstack crashed
watchdog timeout
system restarting...

После перезапуска:

port-channel1: down
re-negotiating LACP

Причина — reboot-loop ToR: crash → перезапуск → повторная инициализация аплинка и таблиц коммутации
В этот момент хосты временно теряют L2-связность

Это объясняет:

отсутствие связности replica-02

недоступность IPMI (mgmt через тот же ToR)

packet loss у других хостов rack-2

App-ноды в rack-1 не затронуты

Retry продолжает маскировать деградацию

09:48

ToR стабилизирован

Replica-02 вернулась в сеть. По необходимости (процессы зависли) хост был перезапущен

pg_stat_replication:
state = streaming
write_lag = decreasing
replay_lag = decreasing

Replication догоняет WAL

09:55

p95 = 45ms
p99 < 120ms
5xx остаётся baseline
Формально простоя не было


Факты периода 09:12–09:48:

  • rack-2 частично изолирован

  • доля повторных попыток выросла в 4–5 раз

  • p99 вырос почти в 10 раз

  • межстоечная отказоустойчивость отсутствовала

  • write-path оставался доступным

Изменения после инцидента:

  • /health дополнен DB round-trip SELECT 1

  • добавлена метрика retry_attempts_total

  • мониторинг питания стойки и состояния ToR интегрирован в систему приоритизации

  • объединён сигнал одновременной недоступности OOB и data-plane

  • правило: потеря реплики в другой стойке повышает приоритет независимо от HTTP 5xx

Что должно триггерить повышение приоритета? Текущая ситуация или потеря отказоустойчивости?

Кто принимает решение об эскалации, когда графики зелёные?

Короткое резюме

Инцидент начался как потеря межстоечной отказоустойчивости. Он стал инцидентом только тогда, когда деградация стала измеримой. Разрыв между этими моментами и есть реальный риск

Об авторе

Работаю с физической и гибридной инфраструктурой на стыке стойки, сети и продакшн-нагрузки. Часто инциденты начинаются не там, где их видит дашборд