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