Перейти к содержанию

Saga Pattern

Раздел 01 · Уровни и общие гарантии

Тип материала: архитектурный разбор. Схемы и примеры иллюстративные, если не указано иное.

Уровень: координация длительного процесса между transaction boundaries. Проверка источников: 2026-11-02.

1. Назначение и решаемая проблема

Saga применяют, когда бизнес-операция охватывает несколько независимых owners и не помещается в одну локальную ACID transaction. Успешные шаги фиксируются отдельно; при отказе выполняются предусмотренные компенсации или процесс переводится в исключительное состояние.

Она не заменяет строгую транзакцию внутри ledger. Если invariant требует, чтобы debit и credit проводки появились атомарно у одного owner, лучше сохранить этот boundary, а Saga использовать вокруг него.

2. Принцип работы

В orchestration durable coordinator хранит состояние и посылает commands участникам. В choreography участники реагируют на events, но владелец общей business completion semantics всё равно должен быть определён.

Каждый шаг имеет identity, idempotent execution, timeout policy и способ выяснить unknown outcome. Compensation — новая бизнес-операция, а не rollback уже закоммиченной transaction. Отправленное письмо, отгруженный товар и некоторые внешние платежи невозможно буквально отменить.

3. Архитектурная схема

flowchart TB
    O["Durable Saga coordinator"] --> H["Reserve funds: локальная transaction"]
    H --> B["Accept bet / external operation"]
    B --> F["Finalize reservation"]
    F --> D["Completed"]
    B -. "Подтверждённый отказ" .-> C["Compensate: release reservation"]
    B -. "Неизвестный результат" .-> Q["Lookup и reconciliation"]
    Q --> O
    C --> X["Compensated либо manual review"]

4. Walkthrough движения данных

  1. Coordinator сохраняет process_id и входной command до запуска работы; повторный запрос находит тот же процесс.
  2. Wallet reserve вызывается с key process_id/step/reserve. Ответ и reservation_id записываются durable.
  3. Следующий owner выполняет business action. Если timeout не позволяет узнать результат, coordinator не считает его отказом: сначала lookup по business key или reconciliation.
  4. При подтверждённом success выполняется finalize; при подтверждённом failure — release hold.
  5. Intent compensation регистрируется безопасно относительно шага: если remote step может выполниться, но ответ потеряться, компенсация должна быть определена и для такого случая. Её handler умеет no-op для отсутствующего эффекта и не отменяет новый независимый action.
  6. Если release временно не удался, процесс остаётся COMPENSATING и повторяет его с тем же key. После budget/deadline — operator queue с полным контекстом.
  7. Completion публикуется только после подтверждённого terminal state, а не после отправки последнего command.

Это учебная модель. Для wallet+bet в общей БД одна transaction может быть безопаснее и проще такой Saga.

5. Технологии реализации

Temporal/Cadence, Step Functions или собственная durable state machine с очередями — варианты orchestration. Kafka/RabbitMQ и outbox/inbox могут обеспечить delivery commands/events. Их наличие не реализует business compensation за разработчика.

FACT: Temporal documentation описывает Saga как local transactions с compensating actions и содержит примеры предварительной регистрации compensation. Документация. SDK replay workflow code и повторное исполнение external activities — разные понятия.

6. Консистентность и гарантии обработки

Нет общей isolation: другие процессы могут увидеть hold или промежуточное состояние. Семантические блокировки, reservations, version checks и допустимые transitions защищают business invariants; они не дают автоматически serializable global history.

Каждый participant должен atomically фиксировать свой effect и idempotency identity. Coordinator durability не обеспечивает однократность external call. Eventual completion также условна: при необратимом эффекте и навсегда недоступном участнике понадобятся бизнес-решение и ручная сверка.

7. Отказы и восстановление

Сбой Recovery Корректность
Coordinator crash Restore durable state/history, повторить незавершённый шаг Participant дедуплицирует
Remote success, response lost Lookup по key; не делать blind compensation Timeout — unknown, не rejected
Compensation падает Durable retry, alert, manual review Не помечать процесс завершённым
Поздний callback после отмены Проверить process version/terminal state Не воскресить отменённую операцию
Stale worker Version/fencing у owner, если требуется Lease coordinator не блокирует все старые writes

Компенсации могут требовать другой порядок из-за бизнес-зависимостей; LIFO — частая модель, не универсальный закон. Для финансового эффекта сохранять ссылку на original posting.

8. Масштабирование и узкие места

Процессы масштабируются по independent process_id, а participants — по собственным invariants. Bottleneck часто находится у wallet/provider, в rate limits и длинных retries. Coordinator history растёт; длинные процессы требуют управления history и retention согласно engine contract.

Большое число pending Saga — не просто очередь задач: это потенциальные holds, открытые обязательства и операционный риск. Метрики: oldest pending, unknown outcomes, compensation backlog и непримирённые суммы.

9. Преимущества и недостатки

Плюсы: нет длительной общей DB transaction, явная модель компенсации, восстановление процесса после crash. Минусы: partial visibility, сложные intermediate states, необратимые actions и необходимость reconciliation.

Saga оправдана бизнес-декомпозицией. Она не должна появляться лишь потому, что одну транзакционную модель преждевременно разбили на microservices.

10. Альтернативные решения

Локальная ACID transaction предпочтительна для tightly coupled invariants. 2PC применим при совместимых participants и принятых availability/operational costs. Durable workflow может завершать процесс forward recovery без компенсации, если это соответствует domain.

Outbox/Inbox решает доставку, а Saga — смысл последовательности; они дополняют друг друга. CQRS может показывать pending state, но не координирует процесс.

11. Реальные примеры и границы подтверждения

FACT: страница технического вебинара Will Bank с Staff Engineer Rabah Zeineddine от 2024-08-08 связывает Temporal, Pix и Saga/compensating transaction problem. Именованный источник.

UNKNOWN: по прочитанной странице нельзя восстановить полный production compensation protocol; видео не просмотрено. Поэтому схема этой статьи — предложенная модель, не архитектура Will Bank. Это подтверждённый контекст применения, но не завершённый case study.

12. Применение в FinTech и iGaming

FinTech: orchestration onboarding/KYC, provider payout и reconciliation; один ledger transfer при этом не дробить без необходимости. Для unknown payment outcome сохранять pending, а не автоматически возвращать деньги и повторять платёж.

iGaming: координация внешнего provider и wallet reservation; при re-settlement компенсация должна ссылаться на конкретную исходную revision. Для market suspension нельзя ждать, что все async шаги мгновенно сойдутся.

13. Типичные ошибки проектирования

  • Выдавать компенсацию за SQL rollback всей системы.
  • Считать timeout доказательством неуспеха внешней операции.
  • Забывать retries/idempotency самой compensation.
  • Завершать Saga, пока release hold ещё не подтверждён.
  • Применять stale callback к новому процессу с тем же entity_id.
  • Прятать финансовый invariant за eventual workflow.

14. Вопросы технического интервью с ответами

Предоставляет ли Saga ACID isolation? Нет; между local commits видны промежуточные состояния. Они должны быть безопасны по domain rules.

Зачем register compensation до remote step? Шаг может выполнить effect и потерять ответ; должны сохраниться сведения для recovery. Compensation при этом должна уметь проверить, был ли effect.

Можно ли всегда компенсировать payout? Нет; иногда возможны только отдельный refund/reversal process, будущий offset либо manual resolution, в зависимости от provider contract.

15. Практическое упражнение

HYPOTHETICAL: reserve подтверждён, внешний bet request вернул timeout, поздний callback сообщает success. Составьте state transitions для lookup-success и lookup-failure.

Ожидается отсутствие одновременно release и finalize одного hold без допустимого domain transition. Добавьте crash coordinator и отказ compensation. Критерий: каждый effect имеет key, terminal state достижим только с evidence, unresolved outcome виден оператору.

Дополнительные источники

Temporal Saga docs; Will Bank webinar scope; Sportradar rollback contract.