Архітектура низької затримки
Навіщо потрібна архітектура низької затримки
Низька затримка - це не тільки «швидке середнє», а стабільні хвости (p95/p99) при реальному навантаженні. Шлях до цього - бюджет затримки, дисципліна черг/ретраїв, близькість даних і кешів, правильні протоколи/коннекти і сувора експлуатація (ліміти, спостережуваність, деградація).
Цілі та бюджет затримки
1. Визначте SLO: "p95 ≤ 120 мс, p99 ≤ 250 мс, помилка ≤ 0. 3%».
2. Зберіть бюджет: клієнт → edge → регіон → сервіси → стори → відповідь.
- Клієнт-edge: 15 мс
- Edge-регіон: 15 мс
- Gateway/L7: 10 мс
- Бізнес-сервіс: 40 мс
- Сховище/кеш: 25 мс
- Запас/джиттер: 15 мс
Будь-який компонент зобов'язаний мати таймаути і план деградації, якщо не вкладається в свій бюджет.
Метрики і хвости
Міряйте p50/p90/p95/p99, наскрізну і на кожному хопі.
Розбивайте по лейблах: регіон, метод, версія клієнта, тип мережі (мобайл/бродбенд), розмір payload.
Розрізняйте час черги і час виконання (див. little's Law: L = λ·W).
Tail-чутливі техніки: hedged requests (рідко і з захистом), заборона каскадних ретраїв.
Мережа та протоколи
QUIC/HTTP/3: менше втрат на мобільних/роумінгу, мультиплексування без head-of-line.
TLS 1. 3 і 0-RTT (тільки для безпечних ідемпотентних запитів).
DNS: короткий TTL для динамічних маршрутів, Anycast для POP.
TCP: 'TCP _ NODELAY'( обачно), відключення зайвих'Nagle '/' Delayed ACK'там, де виправдано; keep-alive і швидке відновлення з'єднань.
gRPC/HTTP/2: мультиплекс, flow-control і налаштування вікон; уникайте надмірної компресії на маленьких payload.
З'єднання і пули
Розділяйте пули за доменами/призначенням (щоб «повільні сусіди» не забирали слоти).
Warm-up/Keep-alive: підтримуйте стійке число теплих конектів.
Connection coalescing (HTTP/2/3) и reuse.
Тайм-аути: `connect`, `TLS handshake`, `request`, `idle`. Різні значення на різних хопах.
Локальність даних та обчислень
Edge/регіон: виносьте читання і легкі обчислення ближче до користувача (див. edge-вузли і регіональна логіка).
Read-local / Write-global: репліки для читання, глобальна істина для запису.
Кеш-ієрархія: CDN/edge-кеш → регіональний KV/Redis → сервісний кеш → локальний in-proc.
Прогрів (warming): завантаження гарячих ключів при релізі/масштабуванні.
Stale-while-revalidate для невисокоризикових даних.
Сховища та індекси
Вибирайте схеми доступу O (1 )/O (logN); тримайте вузькі індекси під часті запити.
Hot-keys: шардуйте по'hash (id)'або додавайте «сіль» для рівномірності.
Batching на виході в БД/кеш (до розумних розмірів) замість десятків одиночних викликів.
Для OLTP - максимально короткі транзакції; read-committed/snapshot замість серійних блокувань.
Конкурентність і безблокувальні прийоми
Спочатку усуньте очікування на чергах, потім оптимізуйте CPU.
Async I/O і неблокуючі драйвери; структури lock-free там, де доречно.
Уникайте глобальних м'ютексів; granular-локи, CAS/версіонування.
Пули потоків: фіксуйте розміри, щоб не впертися в контекст-світчі.
NUMA-усвідомленість: прив'язка потоків до сокетів, локальні алокатори.
JVM/GC і рантайм-тюнінг (якщо застосовується)
Генерація коду та алокації: менше бічних ефектів → менше GC пауз.
Сучасні колектори (G1/ZGC/Shenandoah) з цільовими паузами; escapes і оренди буферів.
Class/Data sharing, JIT warming, AOT/native-image для стартів-залежних функцій.
Гістограми пауз GC включайте в загальний бюджет затримки.
Черги, backpressure, захист від перевантаження
Розмір черг = маленький: довгі черги дають «красиве p50» і вбивають p99.
Явний backpressure: відповідайте «повільніше», ніж копіть.
Adaptive concurrency: знижуйте паралельність при зростанні помилок/латентності (VEGAS/gradient алгоритми, AIMD).
Circuit breaker: швидкі відмови при деградації апстріму, bulkhead (кают-компанії) на пули і ресурси.
Rate limit: ковзне вікно/токени, пріоритизація (user tier/critical-path).
Ретраї, хеджинг та ідемпотентність
Ретраї тільки на транзієнтні помилки, з джиттером і максимумом спроб.
Ідемпотентні операції і «Idempotency-Key» - обов'язкові для повторів.
Hedged requests: відправляйте дублі після порогу (наприклад, p95 + 10 мс) і завжди скасовуйте зайвий.
Ніколи не ретрайте всередині кожного шару без координації - отримаєте шторму.
Кешування і прогрів
Гарячий шлях повинен обходитися без мережі при типовому навантаженні (in-proc/LRU).
Negative cache на 10-60 с, щоб не довбати відсутні ключі.
Масовий прогрів при релізі/скейлінгу: списки гарячих ключів, read-ahead, background refresh.
Деградація і фоллбеки
Graceful Degradation: урізайте другорядні фічі при зростанні латентності (менш деталізована відповідь, відключення збагачень).
Soft timeouts: повертайте базову відповідь/кеш замість 5xx.
Fail-open/Fail-closed - явно задокументуйте для кожного виклику.
Спостережуваність і профілювання
Дистрибутивний трейсинг: спані на кожному хопі, семплінг хвостів (tail-based).
RED/USE метрики: Rate, Errors, Duration / Utilization, Saturation, Errors.
Top-N «повільних» маршрутів щодня.
Профілювальники (alloc/cpu/lock) в проді з низьким оверхедом (eBPF/async-profiler/Flight Recorder).
Синтетика з різних ASN/мереж і мобільних каналів.
Тестування продуктивності
Latency-SLO тести (p95/p99) з реальними payload і варіативністю.
Chaos-сценарії: деградація DNS, зростання втрат пакетів, затримки TLS, «повільний» стор.
Cold-start/scale-up: заміряйте перші хвилини після релізу, коли кеші порожні.
Навантажувальні пули розділяйте за сценаріями (не заважайте read/write тести).
Міні-шаблони
Політика таймаутів/ретраїв (псевдо)
yaml timeouts:
connect: 100ms tls_handshake: 150ms request_p95_budget: 80ms retries:
max_attempts: 2 backoff: exp_jitter(10ms..60ms)
retry_on: [CONNECT_ERROR, TIMEOUT, 502, 503, 504]
hedging:
enabled: true threshold: p95 + 10ms cancel_extra_on_first_success: true circuit_breaker:
error_rate_threshold: 5%
p95_threshold_increase: 30%
half_open_after: 10s
Пули і bulkhead'и
yaml pools:
checkout:
max_conns: 256 per_host: 64 queue: 8 # small analytics queue:
max_conns: 64 queue: 4
Відповідь з деградацією
json
{
"status": "ok",
"profile": { "id": "u123", "name": "…"},
"recommendations": "degraded, "//disabled the heavy part
"served_from": "edge-cache",
"trace_id": "…"
}
Кейси застосування
iGaming/фінанси: авторизація платежу <200 мс p95, ліміти/баланс - читання з регіональних проекцій, записи - ідемпотентні з версією.
Маркетинг/рекомендації: відповіді <100 мс p95, кеш фіч-прапорів на edge, моделі - попередній скоринг + швидкі правила на гарячому шляху.
Мобільні клієнти: HTTP/3, агресивний reuse конектів, зменшені payload (Protobuf), захисні таймаути і offline-кеш.
Анти-патерни
Довгі черги перед воркерами: «красиве середнє» і вбитий p99.
Каскадні ретраї на кожному шарі без координації.
Глобальний «мега-кеш» без інвалідації та прогріву.
Нечіткі таймаути (скрізь «за замовчуванням») - неконтрольовані хвости.
Один загальний пул конектів для всього трафіку - head-of-line блокування.
Важка логіка на edge зі stateful-ефектами.
Відключена телеметрія хвостів - ви «не бачите» p99.
Чек-лист продакшену
- Є бюджет затримки по хопах і таймаути під нього.
- Включені HTTP/2/3, TLS 1. 3, пули конектів і warm-up.
- Ієрархія кешів, список гарячих ключів і стратегії прогріву.
- Read-local/Write-global і шардування гарячих ключів.
- Явний backpressure, маленькі черги, circuit-breakers і bulkhead'и.
- Ретраї з джиттером, ідемпотентність, обмежений хеджинг.
- Трейсинг з мітками регіону/версії/клієнта; моніторинг p95/p99.
- Perf-тести з синтетикою по ASN/мобайлу, сценарії cold-start і chaos.
- Процедури деградації і фоллбеків задокументовані.
- p95/p99 відповідають SLO на реальному навантаженні.
FAQ
Чому p99 важливіше середнього?
Тому що користувачі стикаються з хвостами, а не з середнім. p99 показує, «скільки реально болить».
Чи варто включати хеджинг скрізь?
Ні, ні. Він корисний для рідкісних хвостів в критичних шляхах і тільки при строгих лімітах/ідемпотентності.
Як зменшити холодний старт?
Прогрів кешів/з'єднань, попередня компіляція/JIT-прогрів, мінімізація lazy-ініціалізацій, warm-пули.
Чи можна «перемогти мережу»?
Частково: HTTP/3, edge-POP, Anycast, компактні payload, connection reuse і розумні таймаути.
Підсумок
Архітектура низької затримки - це система домовленостей і дисципліни: бюджет затримки, близькість даних, маленькі черги, передбачувані ретраї, кеш-ієрархії, правильні протоколи і безжальна спостережуваність хвостів. Дотримуючись цих принципів, ви тримаєте p95/p99 у вузді без жертв стабільності і гаманця.