Привет, Хабр! Представляю вашему вниманию перевод статьи "Pattern: Saga" автора Chris Richardson.


Ситуация


Есть приложение, к которому применялся паттерн Database per Service. Теперь у каждого сервиса приложения есть своя собственная база данных. Некоторые бизнес транзакции охватывают сразу несколько сервисов, так что нужен механизм, обеспечивающий согласованность данных между этими сервисами.


Например: давайте представим, что мы разрабатываем интернет магазин, где у клиента есть кредитный лимит. Приложение должно гарантировать, что новый заказ не превышает кредитный лимит клиента. Так как Заказы и Клиенты — различные базы данных, то приложение не может использовать локальные ACID транзакции.


Проблема


Как обеспечить согласованность данных между сервисами?


Решение


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


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



Существует два способа координации саг:


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

Пример: сага, основанная на хореографии



В интернет магазине с использованием саги, основанной на хореографии, создание заказа будет включать следующие шаги:


  1. Order Service (Сервис Заказа) создает Order (Заказ) в статусе pending (в ожидании) и публикует событие OrderCreated (ЗаказСоздан)
  2. Customer Service (Сервис Клиента) получает событие и пытается зарезервировать кредит для заказа. После чего публикует одно из двух событий: CreditReserved (КредитЗарезервирован) или CreditLimitExceeded (КредитныйЛимитПревышен)
  3. Order Service (Сервис Заказа) получает событие и изменяет состояние заказа в approved (подтвержден) или cancelled (отменен)

Пример: сага, основанная на оркестровке



В интернет магазине с использованием саги, основанной на оркестровке, создание заказа будет включать следующие шаги:


  1. Order Service (Сервис Заказа) создает Order (Заказ) в статусе pending (в ожидании) и создает CreateOrderSaga (СагаСозданияЗаказа)
  2. CreateOrderSaga (СагаСозданияЗаказа) отправляет команду ReserveCredit (ЗарезервироватьКредит) в Customer Service (Сервис Клиента)
  3. Customer Service (Сервис Клиента) пытается зарезервировать кредит для заказа и отправляет назад ответ
  4. CreateOrderSaga (СагаСозданияЗаказа) получает ответ и отправляет ApproveOrder (ПодтвердитьЗаказ) or RejectOrder (ОтменитьЗаказ) команду в Order Service (Сервис Заказа)
  5. Order Service (Сервис Заказа) изменяет состояние заказа в approved (подтвержден) или cancelled (отменен)

Сага имеет следующие преимущества


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

Сага имеет следующие недостатки


  • Модель программирования становится более сложной. Например, разработчики должны проектировать компенсирующие транзакции, которые отменяют изменения, сделанные ранее в саге.

Комментарии (4)


  1. Demtriy
    25.10.2018 15:28

    Выглядит как машина состояний, применительно к ACID транзакциям. Windows Flow решает те же самые задачи (не только он), причем делая транзакции не просто «не локальными», а даже, распределенными во времени.


  1. TerekhinSergey
    26.10.2018 10:08

    А какой существует план восстановления, если компенсирующая транзакция не выполнится?


    1. pavzay Автор
      26.10.2018 10:20

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


  1. UnclShura
    26.10.2018 17:31
    +1

    • Прошли раги 1-4 из примера в статье, а OrderService нет вообще запущеного...
    • Прошли раги 1-4 из примера в статье, а OrderService запущен но не отвечает...
    • Прошли раги 1-4 из примера в статье, а OrderService ответил… через трое суток...
    • Прошли раги 1-4 из примера в статье, а OrderService сконфигурирован на UAT базу...


    Это не сага, это — плач.