Сервис оформляет заказ через несколько автономных хранилищ. Как сага обеспечивает согласованность без общей транзакции?
Сага разбивает бизнес-операцию на последовательность локальных транзакций, каждая из которых подтверждается независимо. Если следующий шаг не выполнен, система запускает компенсирующие действия для уже завершённых шагов, поэтому согласованность достигается не атомарностью, а управляемым переходом к согласованному состоянию.
Сага допускает временно видимые промежуточные состояния. Она не даёт тех же гарантий, что единая транзакция с атомарным коммитом, поэтому требует идемпотентности, надёжного хранения состояния процесса, повторов и контроля неуспешных компенсаций.
Распределённые бизнес-операции часто затрагивают несколько сервисов или хранилищ, которыми нельзя управлять одной локальной транзакцией. Попытка сделать единую транзакцию между ними приводит к необходимости координации, удержанию ресурсов и зависимости доступности всех участников.
Саги появились как подход к длительным распределённым операциям, где блокирующая атомарность слишком дорога или технически недоступна. Вместо единого отката всей операции система явно описывает действия, которые логически отменяют уже выполненные шаги.
Например, оформление заказа может включать создание заказа, резервирование товара и списание или блокировку оплаты. Эти операции находятся в разных bounded context и могут завершаться с разной задержкой или временно быть недоступными.
Если после резервирования товара платёж не прошёл, простого отката локальной транзакции платежа недостаточно: резерв уже зафиксирован в другом хранилище. Без компенсации появится несогласованное состояние — заказ не оплачен, но товар недоступен для других покупателей.
Сага представляет операцию как упорядоченный набор локальных шагов. Каждый участник выполняет свою транзакцию и сообщает результат; координатор или следующий участник инициирует дальнейший шаг. Для каждого необратимого на уровне бизнеса действия заранее определяется компенсирующее действие, например отмена резерва или возврат платежа.
Есть два распространённых стиля:
Компенсация не является техническим откатом. Она выполняется отдельной транзакцией и может быть отложенной, частично успешной или невозможной: например, уже отправленный товар нельзя буквально вернуть мгновенным откатом. Поэтому бизнес-процесс должен иметь состояния вроде ожидания, отмены, ручной проверки и завершённой компенсации.
Каждый шаг и каждая компенсация должны быть идемпотентными: повтор одной команды не должен повторно списать деньги или создать второй резерв. Состояние саги нужно надёжно сохранять, а сообщения — повторять после временных ошибок, отслеживая дубликаты, тайм-ауты и необрабатываемые события.
Сага обеспечивает eventual consistency, но не изоляцию между всеми шагами. Пользователи и другие процессы могут увидеть промежуточное состояние, поэтому модель данных и интерфейсы должны явно учитывать статусы операции. Для публикации переходов состояния также требуется надёжная связь локальной записи с отправкой события, иначе сама сага может потерять информацию о завершённом шаге.
Распределённая транзакция, например двухфазный коммит, может дать более сильную атомарность, но требует общего координатора и согласованного участия хранилищ. Это повышает связанность и чувствительность операции к сбоям и задержкам. Сага обычно предпочтительна для длительных бизнес-процессов, где локальная автономность важнее мгновенной атомарности.
Интернет-магазин сначала создаёт заказ, затем резервирует товар на складе и запрашивает авторизацию платежа. Рассматривались два варианта: распределённая транзакция и сага. Распределённая транзакция дала бы единый результат, но потребовала бы участия склада, платёжного сервиса и базы заказов в общей координации; внешняя платёжная система при этом может не поддерживать нужный протокол.
Для саги выбрали оркестратор. После успешного создания заказа он запрашивает резерв, затем авторизацию платежа; при отказе платежа отправляет команду отмены резерва. Повторные команды идентифицируются ключом операции, а зависшие процессы попадают в очередь повторной обработки и на ручной разбор.
Результатом стала автономность участников и отсутствие долгих блокировок. Компромиссом стали промежуточный статус заказа и необходимость отдельно обрабатывать ситуацию, когда компенсация склада временно недоступна или платёж уже нельзя отменить автоматически.
Нет. Компенсация выполняет новую бизнес-операцию и может иметь собственные побочные эффекты. Возврат денег может быть отложен, отмена отправки — невозможна, а уведомление пользователя уже нельзя стереть. Поэтому сага должна моделировать реальные состояния и последствия, а не притворяться мгновенным откатом.
Без идемпотентности возможны повторное списание, дублирование резерва или несколько созданных сущностей. Участник должен проверять уникальный идентификатор операции, хранить результат уже обработанной команды и безопасно возвращать тот же результат при повторе. Одной гарантии доставки недостаточно: повторы являются нормальной частью отказоустойчивой обработки.
Хореография подходит для небольшого числа слабосвязанных шагов, где правила переходов просты. При росте числа участников события начинают образовывать трудно обозримую сеть зависимостей, и диагностика становится сложнее. Оркестрация обычно предпочтительнее для длинных процессов с тайм-аутами, компенсациями и ручными решениями, хотя требует надёжного хранения состояния координатора и не должна превращаться в монолитную бизнес-логику всех сервисов.