GH GambleHub

Сага-паттерн и распределенные транзакции

Сага-паттерн и распределенные транзакции

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 дней

Выделите процессы-кандидаты (мультисервисные, с компенсацией).
Выберите модель (оркестрация/хореография/TCC) и корреляционный ключ.
Опишите шаги/компенсации, инварианты и дедлайны. Поднимите таблицы `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), дедлайны и ретраи, наблюдаемость и процессы компенсации. Выберите модель (оркестрация/хореография/TCC), зафиксируйте инварианты и ключи, сделайте обработчики идемпотентными — и ваши мультисервисные бизнес-процессы станут предсказуемыми и устойчивыми без дорогого 2PC.

Contact

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

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

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

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

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

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