Паттерн Saga

В монолитной архитектуре управлять данными достаточно просто. Обычно работает одна база данных, а транзакциями управляют в рамках одной системы.
В архитектуре микросервисов всё иначе. Каждый сервис работает независимо, и у него есть своя база данных. Транзакции, которые затрагивают несколько баз данных, называют распределёнными. Управлять ими сложнее. Они опасны тем, что могут привести к несогласованности данных.
Допустим, вы разрабатываете онлайн-магазин. Логику оплаты товара и логику доставки вынесли в разные микросервисы. Нельзя допустить, чтобы пользователю отправили товар, если оплата заказа не прошла. Или напротив — магазин списал деньги, но не отправил доставку.
Чтобы избежать таких конфликтов и потери данных, в микросервисных приложениях используют паттерн Saga. Его и разберём в этом уроке.

Что такое паттерн Saga

📎 Saga — это архитектурный паттерн для управления распределёнными транзакциями в микросервисных системах. Он обеспечивает целостность данных и позволяет управлять ошибками при выполнении долговременных бизнес-процессов, которые включают несколько микросервисов.
Напомним, что работа с данными должна подчиняться требованиям ACID:
  • Atomicity — атомарность,
  • Consistency — целостность,
  • Isolation — изолированность,
  • Durability — надёжность.
В монолитных системах за выполнение требований отвечает реляционная СУБД.
В микросервисных архитектурах тоже нужно поддерживать ACID, но использовать механизмы СУБД не получится: процесс включает несколько баз данных. Saga решает эту проблему с помощью набора локальных транзакций и компенсирующих действий. Проще говоря, паттерн дробит транзакцию на шаги, которыми занимаются разные микросервисы. В случае ошибки система активирует отмену действия, которая распространяется на все вовлечённые микросервисы.
Чтобы увидеть различие этих решений, нужно сравнить реализацию ACID в традиционных системах с реализацией в паттерне Saga.

ACID в монолитных системах

Транзакциями управляют на уровне СУБД с помощью языка SQL и встроенных механизмов транзакционного управления. В случае ошибки автоматически происходит откат транзакции (rollback).
ТребованиеОписание
АтомарностьВсе операции в транзакции либо завершаются успешно, либо откатываются.
ЦелостностьЛюбое изменение данных проходит через транзакционные контексты СУБД, которые следят за соблюдением всех ограничений.
ИзолированностьИзоляция транзакций позволяет избежать конфликтов между параллельными транзакциями. Для этого используют уровни изоляции. Например, Read Committed, Repeatable Read и Serializable.
НадёжностьВсе изменения, сделанные в рамках транзакции, записываются на диск и сохраняются даже в случае сбоя системы.

ACID в микросервисах с паттерном Saga

Паттерн можно реализовать двумя способами. Транзакциями управляют через оркестрацию или хореографию. Оркестрация подразумевает наличие отдельного сервиса-оркестратора, а хореография — обмен событиями между микросервисами.
Откат транзакции происходит за счёт вызова компенсирующих действий, которые откатывают изменения, сделанные на предыдущих шагах.
ТребованиеОписание
АтомарностьТранзакции разделяют на последовательные шаги. Каждый шаг — это локальная транзакция в отдельном микросервисе. Атомарность достигается за счёт независимого выполнения каждого шага и применения компенсирующих действий для отката изменений.
ЦелостностьЦелостность данных обеспечивается через компенсирующие действия, которые откатывают предыдущие шаги в случае ошибки. Микросервисы должны координировать между собой состояние транзакции. Иногда это требует дополнительной логики.
ИзолированностьИзолированности достичь уже сложнее: микросервисы работают независимо и могут быть расположены на разных узлах сети. Пока все шаги Saga не завершатся, система может столкнуться с временной неконсистентностью данных.
НадёжностьДолговечность изменений в Saga зависит от отдельных микросервисов и их способности сохранять состояние. Компенсирующие действия должны быть надёжными и обеспечивать сохранение состояния даже при сбоях.

Задание 1

Как требования ACID соблюдаются в рамках паттерна Saga? Выберите все верные утверждения.
Saga предполагает наличие компенсирующих действий, которые запускают в случае ошибки. При монолитной архитектуре атомарность достигается за счёт того, что все операции либо завершаются успехом, либо откатываются.
Надёжность процессов обеспечивается с помощью хранения состояний конфигурации и компенсирующих действий.
Паттерн предполагает разделение данных по разным хостам. Изолированность процессов обеспечивают за счёт использования контейнеризации. Об этом ещё расскажем в уроке подробнее.
А теперь рассмотрим разные реализации паттерна. Поговорим об оркестрации и хореографии.

Типы реализации Saga

Транзакции в паттерне можно координировать двумя способами:
  • через оркестрацию (orchestration),
  • через хореографию (choreography).

Оркестрация

В этом случае есть отдельный сервис-оркестратор. Он управляет последовательностью выполнения шагов и при необходимости инициирует компенсирующие действия.
Допустим, компания разрабатывает банковское приложение. Чтобы одобрить пользователю кредит, нужно пройти через несколько проверок и согласований. В процесс вовлечены такие сервисы:
  • Orchestrator — управляет процессом.
  • Customer Service — проверяет кредитоспособность клиента.
  • Anti-fraud Service — проводит проверку на мошенничество.
  • Approval Service — принимает или отклоняет заявку на основании результатов предыдущих проверок.
Оркестратор координирует все шаги:
  • Начинает с запроса в Customer Service.
  • При этом параллельно может отправить запрос в Anti-fraud Service.
  • После всех проверок принимает окончательное решение с помощью Approval Service.

Когда использовать оркестрацию

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

Хореография

В этом подходе нет центрального управляющего компонента. Каждый микросервис самостоятелен — он знает, что делать после выполнения своего шага. Микросервисы взаимодействуют через события и сами инициируют компенсирующие действия.
Для онлайн-магазина хореографическую сагу можно реализовать так:
  1. Пользователь создаёт заказ — NewOrder — через корзину.
  2. Order Service создаёт заказ и отправляет событие OrderCreated.
  3. Inventory Service получает событие OrderCreated, резервирует товар и отправляет событие InventoryReserved.
  4. Payment Service получает событие InventoryReserved, обрабатывает оплату и отправляет событие PaymentProcessed.
  5. Shipping Service получает событие PaymentProcessed, отправляет товар и отправляет событие OrderShipped.
Когда использовать хореографию
Выбирайте хореографию, если у вас:
  • Простые взаимодействия. Взаимодействие между микросервисами относительно простое и не требует сложной координации. Например, это уведомления по электронной почте или сообщения в чате.
  • Независимые микросервисы. Микросервисы могут работать независимо друг от друга, а значит, изменение одного сервиса не сильно влияет на другие. Например, логирование событий, аудит и аналитика.
  • В приоритете гибкость и масштабируемость. Система должна быть гибкой и легко масштабируемой, а изменения в одном микросервисе не должны требовать изменений в других. Например, вы распространяете маркетинговые предложения через разные каналы.
  • Событийно-ориентированная архитектура. Ваша система построена на событиях и естественным образом поддерживает асинхронное взаимодействие. Например, приложение маркетплейса, где события заказа распространяются на службы оплаты, доставки и уведомления.
Итак, есть два способа управления транзакциями в микросервисах — оркестрация и хореография. Ещё в рамках паттерна можно реализовать разные варианты обработки ошибок.

Обработка ошибок и компенсации в Saga

В паттерне Saga обработка ошибок играет ключевую роль. Каждый шаг транзакции выполняют разные микросервисы, поэтому ошибки могут возникнуть на любом этапе. Чтобы обеспечить целостность данных и корректную работу системы, нужно использовать надёжные техники для обработки ошибок.
Шесть самых популярных стратегий:
  1. компенсационные транзакции,
  2. повторные попытки — их ещё называют «retries»,
  3. тайм-ауты и дедлайны,
  4. Circuit Breaker — эту стратегию переводят как «предохранитель»,
  5. событийно-ориентированная обработка ошибок,
  6. системы мониторинга и алертов.
Их можно комбинировать — даже использовать все и сразу. Рассмотрим каждую стратегию по порядку.

1. Компенсационные транзакции

☝ Это основной метод, который используют в Saga для обеспечения целостности данных. К нему добавляют остальные стратегии.
Компенсационные транзакции — это действия, которые откатывают результаты уже выполненных шагов, если на любом из следующих шагов возникла ошибка. Например, заказ зарезервировали и оплатили, но на этапе отправки возникла ошибка. Компенсирующие транзакции отменят и оплату, и резервирование.
Плюсы:
  • Отмена изменений обеспечивает целостность данных.
  • Можно сохранить результаты успешных операций, если полный откат не нужен.
Минусы:
  • Нужно разрабатывать и тестировать компенсирующие транзакции для каждого шага. Это усложняет разработку.
  • Пока система выполняет компенсирующие транзакции, данные какое-то время могут оставаться неконсистентными.

2. Повторные попытки

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

3. Тайм-ауты и дедлайны

Назначают дедлайн — время, за которое операция должна завершиться. Если этого не происходит, возникает ошибка тайм-аута и операцию отменяют. Например, микросервис пытается получить ответ от другого сервиса. Проходит пять секунд, но ответа нет — запрос отменяется.
Плюсы:
  • Предотвращает бесконечное ожидание ответов от зависимых сервисов.
  • Улучшает отклик системы.
Минусы:
  • Сложно определить оптимальное время тайм-аута.
  • Эта техника не решает проблему, если ошибка постоянная.

4. Circuit Breaker

Если паттерн повторных попыток заточен под временные проблемы, то паттерн Circuit Breaker — под неожиданные и серьёзные. Он работает как предохранитель: система предотвращает попытки выполнить операцию, если известно, что она не будет успешной. Когда количество ошибок превышает определённый порог, дальнейшие попытки блокируют на некоторое время. Например, микросервис не может подключиться к базе данных. В течение минуты он получил 50 ошибок. Circuit Breaker заблокирует дальнейшие попытки подключения на 30 секунд.
Плюсы:
  • Предотвращает перегрузку системы из-за попыток выполнить заведомо неуспешные операции.
  • Улучшает устойчивость системы к ошибкам.
Минусы:
  • Требует настройки порогов и тайм-аутов.
  • Может привести к отказу в обслуживании, если пороги установлены неправильно.

5. Событийно-ориентированная обработка ошибок

Ошибки можно обрабатывать через события. Если в ходе операции в микросервисе возникла ошибка, он опубликует событие о неудаче. Другие микросервисы получат и обработают событие. Например, сервис оформления заказа не может завершить операцию и публикует событие OrderFailed. В зависимости от типа управления саги, это событие обработает оркестратор или следующий сервис в хореографической цепочке — сервис хранения товаров. Он запустит выполнение компенсирующих действий.
Плюсы:
  • Обеспечивает асинхронную обработку ошибок.
  • Улучшает модульность и расширяемость системы.
Минусы:
  • Усложняет отслеживание состояния транзакции и его управление.
  • Требует надёжную инфраструктуру для доставки событий.

6. Системы мониторинга и алертов

Чтобы обнаруживать ошибки и быстро на них реагировать, нужно настроить системы мониторинга — например, Prometheus или Grafana. Они будут отслеживать метрики и журналы ошибок. Алерты оповестят команду поддержки, если возникнут критические проблемы.
Плюсы:
  • Быстрое обнаружение ошибок и оперативная реакция команды.
  • Общая надёжность системы повысится.
Минусы:
  • Нужно настроить и поддерживать системы мониторинга и алертов.
  • Системы могут ложно срабатывать, если их неправильно настроить.

Задание 2

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

Задание 3

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

Задание 4

Вы разрабатываете микросервисную систему для банковского приложения. Если в обработке транзакций крупных сделок появятся ошибки, важно узнать об этом как можно раньше. Что позволит этого достичь?
Системы мониторинга и алертов позволяют быстро обнаруживать ошибки и оперативно реагировать на них, повышая общую надёжность системы.
Итак, вы можете определить, какой тип саги вам нужен, — оркестрация или хореография. А ещё представляете, какие стратегии обработки ошибок можно внедрить для оптимизации ресурсов системы и повышения её надёжности. Осталось разобраться, как внедрить паттерн Saga.

Как внедрить паттерн Saga

Алгоритм состоит из шести шагов. Разберём их по очереди, а чтобы процесс не выглядел абстрактно, покажем некоторые аспекты на примере знакомой компании. Вперёд, «Шайба»!

Шаг 1. Анализ бизнес-процесса и определение шагов Saga

На этом этапе нужно сделать две вещи. Сначала идентифицируйте процесс, а затем определите для каждого шага действия и компенсирующие действия.
Идентифицируйте процесс. Разделите бизнес-процесс на последовательные шаги. Каждый шаг будет выполнять отдельный микросервис.
Определите действия (do) и компенсирующие действия (undo). Для каждого шага определите основное действие и компенсирующее действие, которое нужно выполнить в случае ошибки.
Пример. Для наглядности рассмотрим только процесс создания заказа. В «Шайбе» он состоит из четырёх шагов. За каждый шаг отвечает один сервис:
  1. Формирование заказа — Order Service.
  2. Резервирование товара на складе — Inventory Service.
  3. Оплата заказа — Payment Service.
  4. Отправка товара клиенту — Shipping Service.

Шаг 2. Проектирование микросервисов

Создайте микросервисы. Каждый из них будет отвечать за выполнение одного шага процесса. У микросервисов должны быть чётко определённые API для выполнения действий. При необходимости можно создать отдельные API для вызова компенсирующих действий.
Обеспечьте идемпотентность операций для всех действий и компенсирующих действий. Для этого используйте уникальные идентификаторы транзакций и проверяйте состояние перед выполнением операции, чтобы избежать повторного выполнения.
Пример. У «Шайбы» получится так.
Order Service:
  • Do — создать заказ.
  • Undo — отменить заказ.
Inventory Service:
  • Do — зарезервировать товар.
  • Undo — отменить резервирование.
Payment Service:
  • Do — провести оплату.
  • Undo — вернуть деньги.
Shipping Service:
  • Do — отправить товар.
  • Undo — отменить отправку.

Шаг 3. Управление состоянием и взаимодействием микросервисов

Обеспечьте хранение состояния. Каждый микросервис должен хранить состояние своей части транзакции. Например, в собственной базе данных. Состояние должно включать уникальный идентификатор транзакции и текущий статус операции. Статус операции нужен как раз для того, чтобы избежать повторения уже совершённых действий.
Выберите подход к координации шагов Saga. Можно использовать оркестрацию или хореографию. Если выбираете оркестрацию, создайте отдельный сервис-оркестратор, который будет управлять последовательностью выполнения шагов.
Пример. Алекс — архитектор «Шайбы» — решил, что лучше использовать центральный оркестратор. Оформление заказа как раз относится к сложным бизнес-процессам. Оркестратор заказов будет управлять последовательностью выполнения шагов: отслеживать статус каждого шага и инициировать компенсирующие действия при необходимости.

Шаг 4. Реализуйте действия и компенсирующие действия

На этом этапе нужно написать код в каждом сервисе для действий и компенсирующих действий. При этом важно обеспечить идемпотентность каждой операции.
Основные действия (do). Реализуйте основное действие в каждом микросервисе. Используйте уникальные идентификаторы транзакций и проверяйте состояние перед выполнением действия, чтобы избежать повторного выполнения.
Компенсирующие действия (undo). По аналогии с основными действиями напишите компенсирующие.

Шаг 5. Логирование и мониторинг

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

Шаг 6. Тестирование

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

Инструменты и фреймворки

  • Long Running Actions for MicroProfile — спецификация представляет API для работы служб по координации активности.
  • Axon — это платформа для создания приложений, основанных на трёх концепциях (CQRS, Event Sourcing и DDD).
  • Eventuate Tram Sagas — это фреймворк оркестровки Saga для микросервисов на Java, которые используют JDBC или JPA.
  • Oracle Database — можно использовать как платформу для создания приложений на базе Saga, в облаке или в локальной сети.
  • Seate Saga Mode — это решение для длинных транзакций от компании SEATA.
  • Apache Camel Saga — Saga EIP предоставляет возможность определить серию связанных действий в маршруте Camel, которые должны быть либо успешно завершены, либо компенсированы.

Задание 5

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

Итоги

Успех микросервисной архитектуры во многом зависит от того, насколько эффективно вы сможете управлять данными и транзакциями. В архитектуре микросервисов транзакции носят распределённый характер, потому что в них участвуют разные базы данных.
Паттерн Saga — это мощный инструмент для управления распределёнными транзакциями в архитектуре микросервисов.
В этом уроке вы изучили основные концепции этого паттерна: транзакции делят на шаги и применяют компенсирующие действия в случае ошибок. Реализовать Saga можно через оркестрацию или хореографию. В любом случае важно обеспечить идемпотентность операций.
Ещё обсудили различные техники обработки ошибок: компенсирующие транзакции, повторные попытки, тайм-ауты, паттерн Circuit Breaker, событийно-ориентированную обработку ошибок и системы мониторинга и алертов.

Что ещё почитать про Saga

Вводная статья, где автор рассматривает преимущества оркестрации.
Статья о паттерне в блоге Temporal.
Репозиторий на GitHub с примерами хореографической Saga.
Это последний урок темы. Дальше — проектная работа.