Скандинавские сагиЭто эпические повествования, рассказывающие о событиях, часто охватывающих значительные периоды времени и множество персонажей
Контекст
ПРоблемная Маша:
Мы применили паттерн Database per service. Теперь у каждого сервиса своя БД. А некоторые бизнес процессы охватывают несколько сервисов. Что делать? Такой бизнес процесс ведь должен быть одной транзакцией!!!
Выручающий Саша:
Ну используй 2PC (двухфазный коммит), чтобы гарантировать транзакцию.
ПРоблемная Маша:
Не могу!
На некоторых сервисах есть БД которые не поддерживают 2PC.
Да и мы внедряли Database per service, чтобы сделать микры слабо связанными и легко масштабировать, а не чтобы затруднить масштабирование через 2PC.
И не хотим всех этих проблем решать с 2PC с вечными транзакциями, сетевые сбои у нас не раз в год, а 3PC мы точно не готовы потянуть.
Выручающий Саша:
Ну если мгновенная согласованность (strong consistency) вам не нужна, и достаточно конечно й согласованности (eventual consistency) то примените паттерн Saga - он решит все ваши проблемы.
Проблема
Как сделать транзакцию, которая будет охватывать несколько независимых микросервисов со своими базами данных?
Решение
Реализовать сагу - последовательность локальных транзакций в каждом микросервисе, каждая такая транзакция обновляет базу данных и публикует сообщение/событие, которое запускает следующую локальную транзакцию. Если локальная транзакция завершается неудачно, то происходит цепочка компенсирующих транзакций в обратном направлении, которые отменяют изменения. В итоге такая цепочка локальных транзакций становится одной бизнес транзакцией.
Есть два варианта реализации Saga: хореография, оркестрация.
Хореография
Пришел запрос на покупку товара в Order Service
Order Service публикует Order Created Event
Products Service резервирует у себя в базе товар и публикует Product Reserved Event
Payment Service пробует оплатить, но происходит ошибка Card Authorization Failed Exception
Payment Service публикует событие Card Authorization Failed Event - что инициирует компенсирующую транзакцию
Products Service отменяет бронь продукта в базе данных и публикует Product Reservation Cancelled Event
Order Service отменяет ордер
Оркестрация
Пришел запрос на покупку товара в Order Service
Order Service публикует Order Created Event
Saga читает Order Created Event и публикует команду Reserve Product Command
Products Service резервирует у себя в базе товар и публикует Product Reserved Event
Saga читает Product Reserved Event и публикует команду Process Payment Command
Payment Service пробует оплатить, но происходит ошибка Card Authorization Failed Exception
Payment Service публикует событие Card Authorization Failed Event - что инициирует компенсирующую транзакцию
Saga читает Card Authorization Failed Even и публикует команду Cancel Product Reservation Command
Products Service отменяет бронь продукта в базе данных и публикует Product Reservation Cancelled Event
Saga читает Product Reservation Cancelled Event и публикует команду Reject Order Command