Может ли сага гарантировать атомарный откат изменений во всех сервисах при сбое одного шага?
Нет. Сага не обеспечивает атомарный откат: она выполняет последовательность локальных транзакций, а при сбое запускает компенсирующие действия. Поэтому итоговая согласованность достигается постепенно, а промежуточные изменения могут быть видны другим участникам.
В распределённой системе отдельные сервисы обычно владеют собственными базами данных. Общая транзакция между ними через двухфазный протокол может удерживать ресурсы, зависеть от координатора и плохо работать при сетевых сбоях или длительных операциях.
Сага появилась как способ разделить длительную бизнес-операцию на локальные транзакции. Вместо общего атомарного отката система определяет действия, компенсирующие уже выполненные шаги.
Например, оформление заказа может включать резервирование товара, списание денег и создание доставки. Если резервирование и списание завершились, а создание доставки не удалось, база одного сервиса не может автоматически откатить изменения в других базах.
Неверно считать компенсацию обычным ROLLBACK. Она сама является отдельной операцией и может завершиться с ошибкой, быть выполнена повторно или увидеть уже изменившееся состояние. Кроме того, некоторые действия необратимы: отправленное письмо нельзя надёжно отменить, а физически отгруженный товар нельзя мгновенно вернуть в исходное состояние.
Сага состоит из локальных транзакций и компенсирующих операций. Если шаги обозначить как A, B и C, то при ошибке на C система обычно выполняет компенсации для B и A в обратном порядке: не отменяет единую транзакцию, а проводит новые бизнес-операции, возвращающие состояние настолько близко к исходному, насколько это возможно.
Сагой можно управлять через оркестратор, который хранит состояние процесса и явно вызывает шаги, либо через хореографию, где сервисы реагируют на события друг друга. Оркестратор проще контролировать и наблюдать, но он становится важным компонентом процесса. Хореография уменьшает центральную координацию, однако усложняет понимание зависимостей, диагностику и изменение сценария.
Каждый шаг и компенсация должны иметь устойчивый идентификатор и быть идемпотентными: повтор после тайм-аута не должен приводить к повторному списанию или двойному возврату. Для надёжной публикации событий часто применяют паттерн транзакционного исходящего сообщения: факт изменения и сообщение о нём фиксируются в одной локальной транзакции, а отдельный доставщик повторяет отправку.
Система должна хранить состояние саги: выполненные шаги, попытки, ошибки и результат компенсаций. Нужны повторные попытки с ограничением, дедлеттер или ручная обработка, а также понятные состояния вроде ожидает компенсации и требует вмешательства.
Главный компромисс — ослабление изоляции. Другие сервисы могут увидеть промежуточный результат, поэтому бизнес-процесс должен допускать такие состояния или скрывать их через статусы, резервирование и проверки. Сага обеспечивает не атомарность, а согласованность бизнес-процесса в конечном счёте, если компенсации достижимы и корректно спроектированы.
Интернет-магазин сначала резервирует товар, затем списывает оплату и после этого создаёт доставку. При недоступности службы доставки возможны три варианта.
Двухфазная транзакция дала бы более сильную атомарность, но потребовала бы совместимого участия всех хранилищ, удерживала бы ресурсы и была бы чувствительна к отказу координатора. Хореографическая сага уменьшила бы центральную связанность, но сделала бы порядок действий и обработку исключений менее прозрачными. Простое повторение всей операции опасно двойным списанием и повторным резервированием.
Практичным решением стала оркестрируемая сага: оркестратор фиксирует идентификатор заказа и состояние шага, платёжный сервис поддерживает идемпотентное списание и возврат, а сообщения публикуются через транзакционный исходящий журнал. Если доставка не создаётся, оркестратор отменяет оплату и освобождает резерв. Заказы переходят в состояние завершён, отменён или требует ручного вмешательства, поэтому неудачные компенсации не теряются.
Ответ: Сага не должна считаться автоматически отменённой. Компенсацию нужно повторять безопасным способом, используя идемпотентность и устойчивое хранение состояния. После исчерпания автоматических попыток процесс переводят в состояние, требующее ручного вмешательства или специальной процедуры восстановления.
Важно различать временный сбой и постоянную бизнес-ошибку. Сетевой тайм-аут может означать, что операция уже выполнена, поэтому без идемпотентного ключа повтор способен создать новый побочный эффект.
Ответ: Поздние шаги часто зависят от результатов ранних. Если сначала отменить резервирование товара, а затем попытаться отменить оплату, между этими действиями можно получить состояние, в котором деньги уже списаны, а товар снова доступен для другой покупки.
Обратный порядок не является универсальной гарантией корректности: конкретный бизнес-процесс может требовать другой последовательности или независимых компенсаций. Его используют как безопасное базовое правило для обращения цепочки зависимостей.
Ответ: Идемпотентность лишь делает повтор одной операции безопасным с точки зрения её эффекта. Она не объединяет изменения разных сервисов в единую неделимую транзакцию и не предотвращает промежуточное наблюдение состояния.
Например, повторный возврат денег может корректно оставить один итоговый возврат, но между списанием и возвратом пользователь или другой сервис всё равно мог увидеть временно изменённый баланс. Атомарность потребовала бы, чтобы все изменения стали видимыми одновременно или не стали видимыми вовсе.