GH GambleHub

Timeout и circuit control

1) Зачем это нужно

Системы падают не от одного «фатального» сбоя, а от накопления задержек и «волновых» ретраев. Таймауты ограничивают время ожидания и высвобождают ресурсы, а circuit control (breaker + shedding + адаптивная конкуренция) не дает деградации распространиться по цепочке зависимостей. Цель — держать p95/p99 в целевых границах и сохранять доступность при частичных отказах.


2) Базовые определения

2.1 Виды таймаутов (L7/L4)

Connect timeout — установление TCP/TLS-соединения.
TLS/Handshake timeout — рукопожатия TLS/HTTP2 preface.
Write timeout — отправка запроса (включая тело).
Read timeout — ожидание первого байта ответа и/или всего тела.
Idle/Keep-Alive timeout — неактивное соединение.
Overall deadline — «жесткий» срок на весь запрос (end-to-end).

2.2 Бюджет таймаута (deadline budget)

Выделяем целевой `deadline_total` и делим по стадиям:
  • `ingress (gateway) + authZ + app + DB/cache + outbound PSP`.
Пример для payments `POST` (цель 400 мс):
  • шлюз: 30 мс,
  • приложение: 120 мс,
  • БД: 120 мс,
  • PSP: 100 мс,
  • запас: 30 мс.

2.3 Propagation и отмена

`deadline`/`timeout` должен передаваться вниз по цепочке (контекст, заголовки, gRPC Deadline). При истечении — отмена фоновых операций (abort/ctx cancel), очистка блокировок/семафоров.


3) Стратегии установки таймаутов

1. Сверху-вниз: исходя из SLO и p95 — задать end-to-end deadline, затем разбить на под-таймауты.
2. Идентифицировать «дорогие» пути (загрузка файла, отчеты, внешние PSP) — отдельные более длинные, но лимитированные.

3. Idempotent vs write:
  • idempotent (GET/повторы статуса) — короче, агрессивнее;
  • write/денежные — чуть длиннее, но с однократным повтором и идемпотентностью.
  • 4. Градуировать по планам/тенантам (enterprise может иметь длиннее timeout, но меньший параллелизм).


4) Circuit breaker: модели и параметры

4.1 Политики срабатывания

Failure-rate: доля ошибок ≥ X% на окне N запросов/времени.
Consecutive failures: M подряд неудач.
Slow-call rate: доля вызовов дольше порога T.
Error classes: таймауты/5xx/connection-reset → «фатальные», 4xx — не учитываем.

4.2 Состояния

Closed — пропускает все, накапливает статистику.
Open — мгновенный отказ (экономит ресурсы, не давит зависимость).
Half-open — малые «пробы» (N запросов) для «проверки воды».

4.3 Полезные дополнения

Bulkhead (шпангоуты): пул потоков/соединений на зависимость, чтобы одна не «высосала» все.
Adaptive concurrency: автоматическое ограничение параллелизма (AIMD/Vegas-подобные алгоритмы) по наблюдаемой латентности.
Load Shedding: ранний отказ/деградация при нехватке локального ресурса (очереди, CPU, GC-паузы).


5) Взаимодействие: таймауты, ретраи, лимиты

Сначала deadline, потом ретраи: каждый повтор должен умещаться в общий дедлайн.
Backoff + jitter для повторов; уважать `Retry-After` и retry-budget.
Rate limiting: при открытом breaker — снижайте лимиты, чтобы не усиливать шторм.
Idempotency: обязательна на write-операциях (во избежание дублей при «немых» таймаутах).
Где ретраить: предпочтительно на краю (клиент/шлюз), а не глубоко внутри.


6) Практические целевые значения (ориентиры)

Публичные read API: end-to-end `200–500 мс`, read timeout `100–300 мс`.
Критичные write (платежи): `300–800 мс` e2e; внешний PSP ≤ `250–400 мс`.
Connect/TLS: `50–150 мс` (больше — проблема сети/решолвинга).
Idle: `30–90 с` (мобильные клиенты — короче, чтобы экономить батарею).
Значения корректируйте по p95/p99 и регионам.


7) Конфиги и примеры

7.1 Envoy (cluster + route, псевдо)

yaml clusters:
- name: payments_psp connect_timeout: 100ms type: STRICT_DNS lb_policy: ROUND_ROBIN circuit_breakers:
thresholds:
- priority: DEFAULT max_connections: 2000 max_requests: 2000 max_retries: 50 outlier_detection:
consecutive_5xx: 5 interval: 5s base_ejection_time: 30s max_ejection_percent: 50

routes:
- match: { prefix: "/api/v1/payments" }
route:
cluster: payments_psp timeout: 350ms        # per-request deadline idle_timeout: 30s retry_policy:
retry_on: "reset,connect-failure,refused-stream,5xx,gateways"
num_retries: 1 per_try_timeout: 200ms

7.2 NGINX (периметр)

nginx proxy_connect_timeout 100ms;
proxy_send_timeout  200ms;  # write proxy_read_timeout  300ms;  # read (первый байт/все тело)
keepalive_timeout   30s;
send_timeout     15s;

Быстрый отказ при перегрузке limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_conn addr 50;

7.3 gRPC (клиент, Go-псевдо)

go ctx, cancel:= context.WithTimeout(context.Background(), 350time.Millisecond)
defer cancel()
resp, err:= client.Pay(ctx, req) // Deadline передается вниз

7.4 HTTP-клиент (Go)

go client:= &http.Client{
Timeout: 350 time.Millisecond, // общий дедлайн на запрос
Transport: &http.Transport{
TLSHandshakeTimeout: 100 time.Millisecond,
ResponseHeaderTimeout: 250 time.Millisecond,
IdleConnTimeout: 30 time.Second,
MaxIdleConnsPerHost: 100,
},
}

7.5 Resilience4j (Java, псевдо)

yaml resilience4j.circuitbreaker.instances.psp:
slidingWindowType: TIME_BASED slidingWindowSize: 60 failureRateThreshold: 50 slowCallDurationThreshold: 200ms slowCallRateThreshold: 30 permittedNumberOfCallsInHalfOpenState: 5 waitDurationInOpenState: 30s

resilience4j.timelimiter.instances.psp:
timeoutDuration: 350ms

8) Наблюдаемость и алертинги

8.1 Метрики

`http_client_requests{endpoint, status}`, `client_latency_bucket`

`timeouts_total{stage=connectreadwritedeadline}`
`circuit_state{dependency}`: 0/1/2 (closed/half/open)
`slow_call_rate`, `failure_rate`
`active_concurrency{route, dependency}`
`shed_requests_total{reason}` (load shedding)
`retry_total{reason}`, `retry_budget_used`

8.2 Трейсы

Спаны: ingress → handler → DB/Redis → внешние.
Атрибуты: `timeout_ms_target`, `circuit_state`, `queue_time_ms`.
Экземплары: привязывать пики p99 к конкретным trace-id.

8.3 Алерты

`p99_latency{critical}` > цели X минут подряд.
`timeout_rate{dependency}` скачкообразно > Y%.
Частые переходы в `open`/«флаппинг» breaker.
Рост `shed_requests_total` при высоком CPU/GC.


9) Adaptive Concurrency & Load Shedding

9.1 Идея

Автоматика снижает параллелизм при росте хвостов латентности:
  • AIMD: увеличивать медленно, снижать резко.
  • Vegas-подобный: держать целевую очередь (queue time).
  • Token-based: каждый запрос «сжигает» токен; токены выдаются по измеренной скорости.

9.2 Реализация

Локальные семафоры per-route; цель — удерживать `queue_time` ниже порога.
Глобальный «предохранитель» (предельный RPS/конкурентность) на шлюзе.
При нехватке CPU/соединений — ранний отказ до выполнения логики (429/503 с `Retry-After`).


10) Тестирование и хаос-сценарии

Latency injection: искусственно добавлять 50–300 мс на зависимость.
Packet loss/dup/drop (tc/tbf, Toxiproxy).
Knob turning: уменьшать пулы соединений, повышать нагрузку до saturation.
Kill/Degrade одну зону/шард (частичная недоступность).
Проверки: не «проваливается» ли ретрай-шторм; breaker открывается предсказуемо; не растет ли очередь.


11) Антипаттерны

Один глобальный «read timeout» без детализации connect/TLS/пер-стадия.
Отсутствие общего дедлайна → ретраи выходят за рамки SLO.
Ретраи без джиттера и без retry-budget.
«Вечные» соединения без idle-таймаутов → утечки дескрипторов.
Breaker считает 4xx как фатальные ошибки.
Нет отмены/abort → фоновые работы продолжаются после таймаута клиента.
Слишком длинные таймауты для мобильных/нестабильных сетей.


12) Специфика iGaming/финансов

Критичные write (депозиты/выводы): один короткий повтор с Idempotency-Key, затем `202 Accepted` + polling вместо бесконечных ожиданий.
PSP/банкинг: раздельные политики по провайдеру/региону (некоторые медленнее).
Ответственные платежи и лимиты: при блокировках/ревью — быстрый `423/409`, не растягивать «висящие» операции.
Отчетность/агрегации — запускать асинхронно (batch + статус-ресурс).


13) Чек-лист prod-готовности

  • Определен end-to-end deadline по критическим маршрутам (GET/POST).
  • Разложен бюджет по стадиям; включена propagation дедлайна.
  • Конфиги connect/TLS/read/write/idle таймаутов на шлюзе и клиентах.
  • Circuit breaker с порогами failure-rate и slow-call; корректная half-open логика.
  • Bulkheads на зависимости; лимиты параллелизма per-route.
  • Load shedding до выполнения бизнес-логики при перегрузке.
  • Интеграция с ретраями: backoff+джиттер, retry-budget, уважение `Retry-After`.
  • Идемпотентность write, `Idempotency-Key` и outbox для событий.
  • Метрики: timeout/slow-call/состояние breaker/queue time/конкурентность.
  • Хаос-тесты: инъекция задержек/потерь/сбоев, деградация зон.
  • Документация для клиентов: примеры таймингов, коды ответов, советы по повторам.

14) TL;DR

Дайте каждому запросу жесткий дедлайн, разложите его по стадиям и распространяйте вниз по цепочке. Управляйте отказами через circuit breaker + bulkheads + adaptive concurrency + load shedding. Повторы — только в рамках дедлайна, с джиттером и бюджетом; write — только идемпотентные. Мерьте timeout/slow-call, состояние breaker и `queue_time`, регулярно гоняйте хаос-тесты.

Contact

Свяжитесь с нами

Обращайтесь по любым вопросам или за поддержкой.Мы всегда готовы помочь!

Начать интеграцию

Email — обязателен. Telegram или WhatsApp — по желанию.

Ваше имя необязательно
Email необязательно
Тема необязательно
Сообщение необязательно
Telegram необязательно
@
Если укажете Telegram — мы ответим и там, в дополнение к Email.
WhatsApp необязательно
Формат: +код страны и номер (например, +380XXXXXXXXX).

Нажимая кнопку, вы соглашаетесь на обработку данных.