Rate Limits і контроль навантаження
TL; DR
Надійний контур - це комбінація лімітів і квот на декількох рівнях (edge→BFF→servis), справедливий розподіл ресурсів (per-tenant/ключ/роут), SLO-адаптивний троттлінг і бекпрешер замість мовчазних тайм-аутів. Використовуйте token/leaky bucket для «швидкості», ковзне вікно для бухгалтерських квот, конкурентні ліміти для важких операцій, dynamic throttling при деградації і circuit-breaker до крихких апстрімів. Все - під спостережуваністю і з плейбуками.
1) Навіщо ліміти в iGaming/fintech
SLO і стійкість: захист від лавин ретраїв, піків турнірів/евентів, сплесків платежів.
Справедливість: один тенант або партнер не «висмоктує» весь бюджет.
Антиаб'юз/боти: дорилімітинг логіна/реєстрації, спаму, скрейпінгу каталогів.
Вартість: стримування дорогих викликів (KYC, звіти, агрегації).
Комплаєнс/сумлінне використання: формальні «fair use» квоти в договорах.
2) Таксономія лімітів
3) Алгоритми і де застосовувати
3. 1 Token Bucket (за замовчуванням)
Параметри: «rate» (токенів/сек), «burst» (макс запас).
Відмінно для API read, оплати/статусів, BFF.
При порожньому бакеті → 429 +'Retry-After'.
3. 2 Leaky Bucket (усереднення)
Гарантований «знесення» RPS, корисно для вебхуків, щоб не забити воркери.
3. 3 Fixed Window vs Sliding Window
Fixed - простий, але «межі»; Sliding - чесний облік у вікні (хв/год/добу).
Застосовуйте Sliding для договірних квот.
3. 4 Concurrent Limits
Ліміт одночасно активних завдань. Ідеально для експортів/репортів, KYC-пакетів, переробок.
При нестачі - 429/503 + черга/полінг.
3. 5 Cost/Complexity Limiter
GraphQL/пошук: вважаємо «вартість» за глибиною/кардинальністю/розширенням.
Відсікання/деградація «дорогих» запитів, відповідь з підказкою.
4) Ключі лімітування (dimensioning)
per-tenant (мультиаренда, справедливість),
per-api_key/client_id (партнери),
per-route (критичні мутації жорсткіші),
per-user/device/IP/ASN/geo (антибот/антискрейп),
per-BIN/country (платіжні методи, захист емітентів і провайдерів),
per-method (GET м'якше, POST/PUT суворіше).
Композиція: основний ключ + «мультиплікатор ризику» (новий акаунт, TOR/проксі, високий chargeback-ризик).
5) SLO-адаптивний троттлінг
Включайте dynamic throttling, коли SLO в небезпеці:- Тригери: `p95 latency↑`, `5xx↑`, `queue len↑`, `CPU/IO saturation`.
- Дії: знизити rate/burst, включити outlier-ejection, урізати «дорогі» роути, тимчасовий degrade (без важких полів/агрегацій).
- Повернення: ступінчасто (25→50→100%) при нормалізації сигналів N інтервалів підряд.
6) Інтеграція в архітектуру
API Gateway (edge): первинні rate/quotas, гео/ASN, HMAC/JWT-валідація, 429/' Retry-After'.
BFF/Service Mesh: тонкі per-route/per-tenant ліміти, concurrent-limits, circuit-breakers до апстрімів.
Всередині сервісу: семафори на важкі операції, бекпрешер на чергах, «робочі пули» з bound розміром.
Вебхуки: окремий ingress-ендпоінт з leaky bucket і буфером ретраїв.
7) Конфігурації (фрагменти)
Kong / NGINX-style (rate + burst):yaml plugins:
- name: rate-limiting config:
policy: local minute: 600 # 10 rps limit_by: consumer fault_tolerant: true
- name: response-ratelimiting config:
limits:
heavy: { minute: 60 }
Envoy (circuit + outlier + rate):
yaml circuit_breakers:
thresholds: { max_connections: 1000, max_requests: 800 }
outlier_detection:
consecutive_5xx: 5 interval: 5s base_ejection_time: 30s http_filters:
- name: envoy. filters. http. local_ratelimit typed_config:
token_bucket: { max_tokens: 100, tokens_per_fill: 100, fill_interval: 1s }
filter_enabled: { default_value: 100% }
filter_enforced: { default_value: 100% }
Concurrent-limits (псевдо):
pseudo sema = Semaphore(MAX_ACTIVE_EXPORTS_PER_TENANT)
if! sema. tryAcquire(timeout=100ms) then return 429 with retry_after=rand(1..5)s process()
sema. release()
GraphQL cost guard (ідея):
pseudo cost = sum(weight(field) cardinality(arg))
if cost > tenant. budget then reject(429,"query too expensive")
8) Політики для різних каналів
REST
GET - м'якше, POST/PATCH/DELETE - суворіше; «ідемпотентні» статуси/перевірки можна ретраїти.
Для платежів: ліміти на'auth/capture/refund'per-user/tenant/BIN/країна.
GraphQL
Depth/complexity caps, persisted/whitelisted queries, ліміти на «аліаси».
WebSocket/SSE
Ліміт частоти'subscribe/unsubscribe', кап на кількість топіків, контроль розміру подій і send-queue → при переповненні'policy _ disconnect'.
Вебхукі
Leaky bucket на прийомі, per-sender квоти, dead-letter черга, детерміновані 2xx/429.
9) Зворотній зв'язок клієнтам
Завжди повертайте чіткий 429 з заголовками:- `Retry-After:
` - `X-RateLimit-Limit/Remaining/Reset`
- Для квот - 403 з кодом «quota _ exceeded» і посиланням на апгрейд плану.
- Документація: ліміти в OpenAPI/SDL + сторінки «Fair Use».
10) Моніторинг і дашборди
Метрики:- Хіти лімітів: `rate. limit. hit'за ключами/роутами/тенантами.
- 429/503 доля, latency p50/p95/p99, error rate, queue length, open circuits.
- Fair-share: топ-тенанти по споживанню, «bully detector».
- Вебхуки: прийом/ретраї, drop-rate, середній лаг.
- 429 не більше 1-3% від загального RPS (без ботів).
- p95 добавка лімітера ≤ 5-10 мс на edge.
- Час відновлення після деградації ≤ 10 хв.
sql
SELECT ts::date d, tenant, route,
SUM(hits) AS limit_hits,
SUM(total) AS total_calls,
SUM(hits)::decimal/NULLIF(SUM(total),0) AS hit_rate
FROM ratelimit_stats
GROUP BY 1,2,3
ORDER BY d DESC, hit_rate DESC;
11) Плейбуки інцидентів
Шторм ретраїв (падіння апстріму): включити global throttling, підняти backoff, відкрити circuit-breaker, повернути «швидкі помилки» замість тайм-аутів.
Бот-атака/скрейпінг: жорсткий кап по IP/ASN/гео, включити WAF/JS-челендж, обмежити каталоги/пошук.
Пік турніру/евента: превентивно підняти ліміти читання, знизити «дорогі поля», включити кеш/денормалізацію.
Досил вебхуків від PSP: тимчасовий leaky bucket, пріоритизація критичних типів, розширити dead-letter і ретраї.
12) Тестування та UAT
Навантажувальні: RPS сходами, бурсти × 10 від нормального.
Справедливість: емуляція 1 «жадібного» тенанта - не більше X% глобального бюджету.
Деградація: SLO-адаптація скорочує ліміти і тримає p95 в коридорі.
Граничні кейси: зміна вікна (min→chas), тремтіння годинника (clock skew), масштабування Redis/шардінг ключів.
Контракт: заголовки 429 і Retry-After присутні, SDK коректно бек-оффіт.
13) Сховище для лімітів
In-memory для локальних лімітів (малі кластери).
Redis/Memcached для розподілених (Lua-скрипти для атомарності).
Шардування ключів по хешу; TTL під вікна; бекап-метрика для втрати кеша.
Idempotency: лімітер не повинен ламати ідемпотентні повторні виклики (облік за ключем запиту).
14) Управління політиками (Governance)
Каталог лімітів: хто власник, які ключі/поріг/раціонал.
Feature-flags для швидких перемикачів (crisis mode).
Версіонування політик і RFC-процес на зміни договірних квот.
A/B експерименти на підбір оптимальних порогів.
15) Анти-патерни
Глобальний один ліміт «на всі API».
Тільки фіксовані вікна → «крайові» стрибки.
Ліміт без зворотного зв'язку (немає'Retry-After '/headers).
Мовчазні тайм-аути замість швидких 429/503.
Відсутність per-tenant fair-share - один клієнт душить інших.
Немає захисту GraphQL/пошуку за складністю.
Нулі в concurrent-guard → «пилосос» БД/PSP.
16) Міні-шпаргалка вибору
Типово: token bucket (rate+burst) per-tenant+route.
Квоти за грошима/звітами: sliding window добу/місяць.
Важкі операції: concurrent-limits + черга.
GraphQL/пошук: complexity-budgets + persisted queries.
WS/вебхукі: leaky bucket + backpressure.
Криза: dynamic throttling + circuit-breaker + degrade.
Резюме
Контроль навантаження - це багаторівнева дисципліна: правильні алгоритми (bucket/вікна/конкурентність), справедливі ключі лімітування, SLO-адаптація і прозорий зворотний зв'язок. Вшивши ліміти в gateway/mesh/сервіси, озброївши GraphQL/WS/вебхуки профільними полісами і підключивши спостережуваність з плейбуками, ви перетворюєте пікові події і чужі збої в керовані ситуації - без крашею, зірваних виплат і просадок конверсії.