Иногда инцидент начинается не с 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

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

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

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

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

Об авторе

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