Як побудувати fail-safe обробку мільйонів транзакцій в день
Повний текст статті
1) Що означає fail-safe для транзакцій
Fail-safe - це коли будь-яка збійна ситуація призводить або до безпечної зупинки, або до компенсованого стану без втрати грошей і даних. Цілі:- «Подвійних дебетів/кредитів» = 0.
- Втрачених транзакцій/подій = 0.
- Передбачувані SLO по латентності/доставці, чіткі режими деградацій і DR.
Основа - грошові інваріанти (істина балансу в одному місці), ідемпотентність, узгоджена доставка подій.
2) Архітектурні принципи (коротко)
1. Single source of truth: баланс і бухгалтерія - в Ledger/Wallet. Сервіси навколо тримають стану процесів, а не гроші.
2. Idempotency everywhere: всі операції «запису» приймають'Idempotency-Key'; повтор повертає той же результат.
3. Подійність з гарантією доставки: outbox/CDC, черги, DLQ, дедуп.
4. Саги і компенсації, а не «ручні правки».
5. Back-pressure і пріоритети: система сповільнюється, але не руйнується.
6. Спостереження за замовчуванням: структуровані логи, трейсинг, метрики.
7. Мульти-регіон і DR: актив-актив/актив-пасив, регулярні навчання.
3) Референс-топологія
Edge/API GW ──Command API ──App Service (Sagas)
│           │
│         (Outbox TX)
RateLimit     Outbox Table ──Publisher ──Kafka/Pulsar ──Consumers
│                      │
WAF                     └─DLQ/Replay
│
└─Ledger/Wallet (ACID, idempotent debit/credit)
│
└─CDC/Changefeed ──DWH/BI/ReconКлючові місця: Outbox (атомарний запис команди і «чернетки» події), Publisher (рівно-одна доставка), Consumers (ідемпотентні, з дедуп-ключем), DLQ/Replay (контрольовані повтори).
4) Грошові інваріанти та узгодженість
Істина за балансом - Ledger (ACID, серіалізовані транзакції або суворе впорядкування за рахунком).
Грошові команди: «debit», «credit», «hold», «commit», «rollback» - ідемпотентні.
Комбіновані процеси будуються як саги:- 'authorize → settle → credit'( депозит/сеттлмент),'request → submit → settled/failed'( платіж/вивід),'refund/void'( компенсації).
- Ніяких прямих правок балансів в обхід Ledger.
5) Ідемпотентність: дизайн ключів
Ключ повинен однозначно ідентифікувати бізнес-операцію:- `bet_id+amount+currency`, `payment_intent+capture_id`, `payout_id`, `chain_txid`.
- Зберігати результат за ключем (response cache). Повтор з тим же ключем → той же body/статус.
- Контролюйте невідповідність: однаковий ключ з іншою сумою →'IDEMPOTENCY _ MISMATCH'.
6) Черги, порядок і дедуп
Exactly-once ефекти досягаються не транспортом, а ідемпотентними консьюмерами + дедуп-сховищем (LRU/Redis/DB c TTL).
Зберігайте порядок за ключем (partition key ='account _ id/round _ id/player _ id').
Для «різнорідних» ключів - версіонування стану і комутатори (state machine per entity).
DLQ обов'язкова: після N спроб - в ізольовану тему з людиночитаною причиною.
7) Outbox/CDC: чому події «не губляться»
В рамках однієї транзакції в БД сервісу записуємо і бізнес-зміну, і запис в outbox.
Окремий publisher зчитує outbox і публікує в шину з підтвердженням.
Альтернативно - CDC (Change Data Capture) на рівні БД (Debezium/лог реплікації).
Ніяких «логів подій» повз транзакцію - це джерело втрат.
8) Back-pressure і пріоритети
Токен-бакети і квоти на вході (per tenant/brand/region).
Черги з пріоритетом: грошові шляхи вище промо/телеметрії.
При перевантаженні: режими'no new sessions/requests', заморожування вторинних фіч, збереження ядра.
Авто-деградації: урізати частоту фонових завдань, динамічно розширювати критичні воркери.
9) Багаторегіональна стійкість
Актив-актив для API і черг, локальний Ledger (або глобальний з шардингом по регіону/валюті).
Data residency: гроші/PII/журнали не кросіруются без явних правил.
Реплікація подій міжрегіонально - асинхронна, з позначкою «region».
RPO/RTO: цілите RPO ≤ 5 хвилин, RTO ≤ 30 хвилин; регулярно перевіряйте.
10) SLO/SLI і дашборди
Орієнтири (приклад):- p95'authorize/debit/credit'< 150-300 мс (внутрішній шлях).
- p95 end-to-end «komanda→sobytiye в шині» <1-2 с.
- Доставка вебхуків/зовнішніх подій p99 <5 хв.
- «Втрачених/дубльованих транзакцій» = 0 (контрактні перевірки).
Метрики: latency p50/p95/p99, error-rate (4xx/5xx/business), consumer/queue lag, retry storms, settle lag, webhook lag, размер DLQ, частота `IDEMPOTENCY_MISMATCH`.
11) Спостережуваність і аудит
Структуровані логи JSON з'trace _ id','idempotency _ key', бізнес-ID, кодами помилок.
OpenTelemetry: трейсинг HTTP/gRPC/DB/шини, спані саг.
WORM-аудит: незмінні журнали критичних змін (ліміти, ключі, конфіги промо/джекпотів).
Маскування PII/секретів, регіональні бакети, RBAC/ABAC на доступ до логів.
12) Тестування надійності
Контрактні тести: повтор/дублікати, out-of-order, ідемпотентність, дедуп.
Навантажувальні: профіль піків (x10), стійкість черг і БД.
Хаос-кейси: падіння Ledger/гаманця, відвал черги/регіонів, затримки CDC, «шторм» ретраїв.
Game Days: регулярні навчання DR і інцидентів, з заміром MTTR.
13) Сховища та дані
OLTP під гроші: транзакційна БД (RPO≈0), строгі індекси, серіалізовані рівні за критичними сутностями.
Кеш (Redis) - тільки для прискорення, не для «істини». TTL + jitter, захист від cache stampede.
OLAP/DWH - для звітів/аналітики. Потоки з CDC/шини, без навантаження на OLTP.
Схеми даних версіонуються; міграції без даунтайму (expand/contract).
14) Оркестрація ретраїв
Експоненціальний backoff + джиттер, дедлайни/timeout на RPC.
Ідемпотентний повтор на кожному шарі (клієнт → сервіс → споживач).
Квоти на ретраї, захиститися від «штормів» (circuit breaker, hedged requests де доречно).
Replay з DLQ тільки в «безпечні» вікна, з обмеженням швидкості.
15) Безпека транспортів
mTLS скрізь S2S, короткоживучі токени (OAuth2 CC), підписи тіл (HMAC/EdDSA) для вебхуків.
Секрети в Vault/HSM, ротація, ключі per brand/region.
Політики least privilege, «чотири ока» на ручні операції.
16) Примірні контракти (фрагменти)
Ідемпотентна команда дебету
POST /v1/wallet/debit
Headers: X-Idempotency-Key: debit_pi_001, X-Trace-Id: tr_a1b2
{
"account_id":"acc_42",  "amount":{"minor_units":5000,"currency":"EUR"},  "reason":"payout",  "reference_id":"po_001"
}
→ 200 { "status":"committed", "entry_id":"e_77" }
(повтор → ту ж відповідь)Подія з outbox
json
{
"event_id":"uuid",  "event_type":"wallet. debit. committed",  "occurred_at":"2025-10-23T16:21:05Z",  "account_id":"acc_42",  "amount_minor":5000,  "currency":"EUR",  "reference_id":"po_001",  "idempotency_key":"debit_pi_001",  "schema_version":"1. 3. 0"
}17) Чек-листи
Платформа/оператор
- Істина по балансу - один Ledger; немає обхідних шляхів.
- Всі write-операції з'Idempotency-Key'; зберігається відповідь по ключу.
- Outbox/CDC на всі доменні записи, DLQ і керований replay.
- Черги з пріоритетами, back-pressure, режими деградацій.
- Partition-keys обрані за бізнес-ключами; споживачі ідемпотентні.
- SLO-дашборди, OpenTelemetry, WORM-аудит.
- Регулярні DR/xaoc-навчання, контрактні/навантажувальні тести.
- Data residency, шифрування, Vault/HSM, ротація ключів.
Провайдери/інтеграції
- Відправляю'Trace-Id '/' Idempotency-Key', готовий до повторної доставки.
- Вебхуки підписані і дедуплікуються.
Версії схем/контрактів дотримуються (semver, deprecation).
18) Червоні прапори (анти-патерни)
Баланс змінюється по вебхуку без команди в Ledger.
Відсутність ідемпотентності → подвійні списання/кредити.
Публікація подій в обхід outbox/CDC.
Моноліт без back-pressure: пік трафіку валить все.
Змішування OLTP і звітів: BI б'є в бойову БД.
Відсутність DLQ/реплея; «тихе» проковтування помилок.
Немає регіональної ізоляції PII/грошей; загальні ключі на кілька брендів.
Ручні правки балансів/статусів в БД.
19) Підсумок
Fail-safe обробка мільйонів транзакцій в день - це про інваріанти і дисципліну: єдине джерело істини, ідемпотентні команди, саги і outbox/CDC, порядок і дедуп в чергах, спостережуваність і керовані деградації. Додайте мандати доступу, DR-практики і регулярні навчання - і отримаєте систему, де гроші рухаються швидко і тільки один раз, події не губляться, а зростання трафіку і збої стають керованими ризиками, а не сюрпризами.
