АналитикаСистемный анализСистемный аналитик

Как согласовать бизнес операцию, затрагивающую несколько сервисов, если общей транзакции между ними нет?

Как согласовать бизнес-операцию, затрагивающую несколько сервисов, если общей транзакции между ними нет?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Используют распределённую сагу: бизнес-операцию разбивают на локальные транзакции сервисов, а для каждого выполненного шага определяют продолжение или компенсирующее действие. Сага не даёт мгновенной атомарности, поэтому система некоторое время может находиться в промежуточном состоянии, но в итоге либо завершает операцию, либо переводит её в согласованное компенсированное состояние.

Исторический контекст

В монолитной системе несколько изменений обычно можно выполнить в одной транзакции базы данных. При разделении системы на сервисы данные оказываются в разных базах, а общая транзакция становится технически сложной, плохо масштабируется и связывает сервисы сильнее, чем нужно.

Сага появилась как способ управлять длительными бизнес-операциями без удержания общей блокировки и без требования к единому менеджеру распределённых транзакций. Её исходная проблема — согласовать последовательность локальных изменений при частичных сбоях сети, сервисов или инфраструктуры.

Постановка проблемы

Например, оформление заказа включает резервирование товара, списание денег и создание доставки. После успешного резервирования платёжный сервис может стать недоступен, а после списания денег сервис доставки может отклонить заказ.

Если просто повторять всю операцию целиком, можно повторно списать деньги или создать несколько резервов. Если не обрабатывать отказ, система оставит заказ, деньги и остатки товара в противоречивых состояниях.

Подробное решение

Сначала определяют шаги саги и их локальные транзакции. Для каждого шага фиксируют успешное завершение, возможные причины отказа, допустимые повторы и компенсирующее действие: отмена резерва, возврат платежа или отмена заявки на доставку.

Координация бывает двух основных видов. При оркестрации отдельный координатор явно запускает шаги, отслеживает их состояние и инициирует компенсации. При хореографии сервисы реагируют на события друг друга; это уменьшает роль центрального компонента, но усложняет понимание общего процесса, контроль циклов и диагностику.

Компенсация не является техническим откатом. Она выполняется отдельной бизнес-операцией и может сама завершиться не сразу или оказаться невозможной. Поэтому состояние саги, результаты шагов и ошибки нужно хранить явно, включая состояния «выполняется», «завершена», «компенсируется» и «требует ручного вмешательства».

Каждый обработчик шага и компенсации должен быть идемпотентным или защищённым уникальным бизнес-идентификатором. Это необходимо из-за повторной доставки сообщений, тайм-аутов и ситуации, когда действие выполнено, но подтверждение не дошло до координатора.

Для надёжности обычно разделяют фиксацию изменения в локальной базе и отправку сообщения через надёжный механизм, например outbox. Однако outbox решает доставку события из конкретного сервиса, а не согласование всей бизнес-операции.

Главный компромисс саги — отказ от мгновенной глобальной атомарности в пользу доступности и независимости сервисов. Если бизнес не допускает даже кратковременного промежуточного состояния, следует пересмотреть границы сервисов или отдельно оценить применение распределённой транзакции; сага не заменяет её во всех сценариях.

Ситуация из практики

В интернет-магазине нужно зарезервировать товар, принять оплату и передать заказ в доставку. Рассматривались три варианта: общая распределённая транзакция, хореография событий и оркестрация саги.

Общая транзакция давала более строгую атомарность, но связывала независимые сервисы, требовала их совместного участия и плохо соответствовала длительным операциям. Хореография снижала центральную связанность, но усложняла контроль последовательности и расследование зависших заказов.

Выбрали оркестрацию саги. Координатор последовательно запускал резервирование, оплату и доставку, хранил состояние каждого шага, а при отказе доставки инициировал возврат платежа и снятие резерва. Повторные команды обрабатывались по идентификатору операции, а заказы, для которых компенсация не завершилась автоматически, попадали в очередь ручной обработки.

Такой вариант не сделал процесс мгновенно атомарным, но обеспечил наблюдаемое состояние операции, предсказуемую обработку частичных отказов и отсутствие повторных бизнес-действий при повторах сообщений.

Что кандидаты часто упускают

  1. **Почему компенсация не всегда равна обратному действию?

Потому что между исходным действием и компенсацией могли произойти новые бизнес-события. Например, товар уже мог быть отгружен, а возврат платежа может требовать отдельной проверки. Компенсация должна быть самостоятельной бизнес-операцией с собственными правилами, статусами и обработкой ошибок, а не механическим вызовом «отменить всё».

  1. **Что делать, если сервис не ответил, но действие, вероятно, уже выполнил?

Нельзя автоматически считать действие неуспешным и безусловно повторять его. Координатор должен использовать уникальный идентификатор операции, поддерживать запрос статуса или безопасный повтор и различать подтверждённый успех, подтверждённый отказ и неизвестный результат. Без этого возможны двойное списание, повторное резервирование или ошибочная компенсация.

  1. **Как определить, что сага окончательно завершена?

Нужно заранее определить терминальные состояния процесса: успешное завершение, полная компенсация и состояние, требующее ручного вмешательства. Тайм-аут сам по себе не доказывает отказ: он лишь означает отсутствие ответа в заданный момент. Поэтому окончательное состояние должно устанавливаться по подтверждённому результату шага, согласованной политике повторов или процедуре разбора неопределённого состояния.