Сага-патерн і розподілені транзакції
Сага-патерн і розподілені транзакції
1) Навіщо потрібні саги
Класичний 2PC (двофазна фіксація) погано масштабується, складний під відмовами і блокує ресурси. Сага розбиває загальний бізнес-процес на послідовність локальних транзакцій (кроків), кожна з яких коммітиться незалежно. При збої наступні кроки скасовуються, а вже виконані - компенсуються зворотними операціями.
Результат: керована eventual consistency без глобального блокування, висока живучість і чіткий протокол відновлення.
2) Базові моделі
2. 1 Оркестрація
Виділений координатор саги керує кроками: посилає команди, чекає відповіді/події, ініціює компенсації.
Плюси: централізований контроль, проста спостережуваність, явні дедлайни. Мінуси: додатковий компонент.
2. 2 Хореографія
Немає координатора; сервіси реагують на події один одного («OrderPlaced» → «PaymentCaptured» → «InventoryReserved»...).
Плюси: Слабка зв'язність. Мінуси: складніше простежувати, ризик «танцю смерті» без чітких правил.
2. 3 TCC (Try-Confirm/Cancel)
Варіант з «заморожуванням» ресурсів:1. Try - підготовка/резерв,
2. Confirm - фіксація,
3. Cancel - відкат.
Гарантії вище, але складніше контракти і таймаути резервів.
3) Контракти кроків і компенсації
Кожен крок = локальна транзакція + компенсація (ідемпотентна, допускає повтор).
Компенсація не зобов'язана повністю «повернути мир» - достатньо доменної еквівалентності (наприклад, «виплата повернення» замість «видалити платіж»).
Визначте інваріанти: для грошей - баланс не йде в мінус; для замовлень - немає «завислих» статусів.
Вводьте дедлайни/TTL резервів і «garbage collector» для прострочених спроб.
4) Узгодженість і семантики доставки
Доставка повідомлень: at-least-once (дефолт) → всі операції повинні бути ідемпотентними.
Порядок: важливий за кореляційним ключем (наприклад,'order _ id','player _ id').
Exactly-once - не мета саги; домагаємося ефективної рівно-однократності через ідемпотентні ключі, outbox/inbox і коректне комітування.
5) Стан саги та її лог
Що зберігати:- 'saga _ id','correlation _ id', поточний статус (Running/Completed/Compensating/Compensated/Failed),
- крок і його змінні (IDs платежів/резервів),
- історію (журнал) подій/рішень, таймстемпи, дедлайни, число ретраїв.
- Окремий Saga Store (таблиця/документ), доступний координатору.
- Для хореографії - локальні «агенти» саги, що публікують події статусу в загальний топік.
6) Патерни надійної публікації: outbox/inbox
Outbox: крок комітить зміни і записує подію/команду в таблицю outbox в одній транзакції; воркер публікує в шину.
Inbox: споживач веде таблицю оброблених'message _ id'→ дедуп + ідемпотентність.
Після успішного побічного ефекту коммітимо offset/ACK (Kafka/RabbitMQ) - не раніше.
7) Проектування кроків саги
7. 1 Приклад (покупка в iGaming/e-commerce)
1. PlaceOrder → статус'PENDING'.
2. AuthorizePayment (Try) → `payment_hold_id`.
3. ReserveInventory → `reservation_id`.
4. CapturePayment (Confirm).
5. FinalizeOrder → `COMPLETED`.
- якщо (3) провалився →'CancelPaymentHold';
- якщо (4) провалився після (3) → «ReleaseInventory»;
- якщо (5) провалився → «RefundPayment» і «ReleaseInventory».
7. 2 Дедлайни/ретраї
Максимум N ретраїв з експоненціальною затримкою + джиттер.
Після перевищення - перехід в'Compensating'.
Зберігайте next_attempt_at і deadline_at для кожного кроку.
8) Оркестратор vs платформа
Варіанти:- Легкий домашній оркестратор (мікросервіс + таблиця Saga).
- Платформи: Temporal/Cadence, Camunda, Netflix Conductor, Zeebe - дають таймери, ретраї, довгоживучі воркфлоу, видимість і веб-консоль.
- Для хореографії використовуйте каталог подій і сувору угоду про статуси/ключі.
9) Протоколи інтеграції
9. 1 Асинхронно (Kafka/RabbitMQ)
Команди: `payments. authorize. v1`, `inventory. reserve. v1`.
Події: `payments. authorized. v1`, `inventory. reserved. v1`, `payments. captured. v1`, `payments. refunded. v1`.
Ключ партії ='order _ id '/' player _ id'для порядку.
9. 2 Синхронно (HTTP/gRPC) всередині кроку
Допустимо для «коротких» кроків, але завжди з таймаутами/ретраями/ідемпотентністю і fallback в асинхронну компенсацію.
10) Ідемпотентність і ключі
У запитах команд і компенсацій передавайте'idempotency _ key'.
Побічні ефекти (запис в БД/списання) виконуються умовно: «виконати якщо ще не бачили'idempotency _ key'».
Компенсації теж ідемпотентні: повтор'RefundPayment (id = X)'безпечний.
11) Обробка помилок
Класи:- Transient (мережі/таймаути) → ретраї/backoff.
- Business (недостатньо коштів, ліміти) → негайна компенсація/альтернативний шлях.
- Irrecoverable (порушення інваріанта) → ручне втручання, «ручна» компенсація.
- Вибудовуйте матрицю рішень: тип помилки → дію (retry/compensate/escalate).
12) Спостережуваність і SLO саг
SLI/SLO:- End-to-end latency саги (p50/p95/p99).
- Success rate (частка завершених без компенсацій).
- Mean time to compensate и compensation rate.
- Orphaned sagas (висять) і час до GC.
- Трасування: 'trace _ id '/' saga _ id'як span link між кроками; метрики burn-rate для бюджетів помилок.
Логи: кожна зміна статусу саги = структурований запис з причиною.
13) Приклади (псевдокод)
13. 1 Оркестратор (ідея)
python def handle(OrderPlaced e):
saga = Saga. start(e. order_id)
saga. run(step=authorize_payment, compensate=cancel_payment)
saga. run(step=reserve_inventory, compensate=release_inventory)
saga. run(step=capture_payment, compensate=refund_payment)
saga. run(step=finalize_order, compensate=refund_and_release)
saga. complete()
def run(step, compensate):
try:
step () # local transaction + outbox except Transient:
schedule_retry()
except Business as err:
start_compensation(err)
13. 2 Outbox (ідея таблиці)
outbox(id PK, aggregate_id, event_type, payload, created_at, sent_at NULL)
inbox(message_id PK, processed_at, status)
saga(order_id PK, state, step, next_attempt_at, deadline_at, context JSONB)
saga_log(id PK, order_id, time, event, details)
13. 3 Хореографія (ідеї тем)
`orders. placed'→ споживачі: `payments. authorize`, `inventory. reserve`
`payments. authorized` + `inventory. reserved` → `orders. try_finalize`
Будь-яка відмова →'orders. compensate'→ ініціюються'payments. cancel/refund`, `inventory. release`.
14) Порівняння з 2PC і ES
2PC: сильна узгодженість, але блокування, вузькі місця, «мідні труби».
Сага: eventual consistency, потрібна дисципліна компенсацій і телеметрії.
Event Sourcing: зберігає події як джерело істини; саги на ньому природні, але додають складність міграцій/снапшотів.
15) Безпека та комплаєнс
Сек'юрність транспорту (TLS/mTLS), ACL per topic/queue.
У подіях - мінімум PII, шифрування чутливих полів, токенізація.
Аудит доступу до саг і журналів компенсацій.
SLA з зовнішніми провайдерами (платежі/доставка) = параметри дедлайнів і лімітів ретраїв.
16) Чек-лист впровадження (0-45 днів)
0-10 днів
Виділіть процеси-кандидати (мультисервісні, з компенсацією).
Виберіть модель (оркестрація/хореографія/ТСС) і кореляційний ключ.
Опишіть кроки/компенсації, інваріанти та дедлайни. Підніміть таблиці «saga», «outbox», «inbox».
11-25 днів
Увімкніть outbox/inbox, ідемпотентність і ретраї з backoff.
Деплойте перші саги; додайте дашборди SLI/SLO і трасування.
Напишіть runbook компенсацій (в т.ч. ручних) і ескалацій.
26-45 днів
Автоматизуйте GC «висять» саг, періодичні перезапуски/продовження по дедлайну.
Проведіть game-day: відмова кроку, перевищення дедлайну, недоступність брокера.
Стандартизуйте контракти подій (версії, сумісність), заведіть «каталог саг».
17) Анти-патерни
«Компенсація = delete з БД» замість доменно коректної зворотної дії.
Немає outbox/inbox → втрата подій/подвійні ефекти.
Ретраї без джиттера → само-DDoS залежностей.
Очікування сильної узгодженості на читанні без «йде обробка»....
Один гігантський оркестратор на всі → моноліт управління.
Тотальна хореографія без видимості і SLA → некерований танець.
Ігнорування дедлайнів → вічні резерви/холди.
18) Метрики зрілості
≥ 90% критичних процесів покриті сагами/компенсаціями і мають описані інваріанти.
Outbox/inbox інтегровані для всіх продьюсерів/консьюмерів Tier-0/1.
SLO: p95 end-to-end саги в нормі, success rate стабільний, orphaned <цільового.
Прозора трасування і дашборди «по кроках», burn-rate алерти.
Щоквартальний game-day і перевірка ручних runbook-компенсацій.
19) Висновок
Сага - це практичний контракт узгодженості для розподілених систем: чіткі кроки і зворотні дії, дисципліна публікації (outbox/inbox), дедлайни і ретраї, спостережуваність і процеси компенсації. Виберіть модель (оркестрація/хореографія/ТСС), зафіксуйте інваріанти і ключі, зробіть обробники ідемпотентними - і ваші мультисервісні бізнес-процеси стануть передбачуваними і стійкими без дорогого 2PC.