open atlas
↑ К треку
Архитектурные паттерны ARCH · 09 · 02

Саги

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

ARCH Senior ◷ 25 min
Уровень
ОсновыJuniorMiddleSenior

Биллинговой платформе нужно было обрабатывать размещение заказа как межсервисную операцию: зарезервировать товар, списать платёж, обновить статус заказа — всё или ничего. Команда попробовала распределённую транзакцию через двухфазный коммит (2PC). В стейджинге всё работало. В продакшне платёжный шлюз иногда отвечал восемь секунд. Распределённая транзакция удерживала блокировки на записи инвентаря все эти восемь секунд. Под нагрузкой таймауты блокировок каскадировались: блокировки инвентаря голодали другие заказы, таймауты триггерили ретраи, ретраи накапливали новые блокировки. Система встала. Они отказались от 2PC и реализовали сагу — последовательность локальных транзакций, каждая коммитится независимо, с явными компенсирующими действиями для отмены предыдущих шагов при сбое последующих.

Почему 2PC не работает в распределённых системах

Двухфазный коммит (2PC) обеспечивает ACID-атомарность между несколькими участниками: либо все коммитятся, либо все откатываются. Координатор просит всех участников подготовиться (фаза 1), ждёт подтверждения от всех, затем отправляет commit или abort (фаза 2). Если кто-то из участников говорит «нет» в фазе 1, координатор откатывает всех.

Это звучит корректно. Проблема — операционная:

Долгоудерживаемые блокировки. В фазе 1 каждый участник удерживает свои локальные ресурсы заблокированными до получения сообщения commit или abort из фазы 2. Если платёжный шлюз отвечает 8 секунд, блокировка инвентаря удерживается 8 секунд. При высокой параллельности медленные участники блокируют несвязанные транзакции.

Сбой координатора. Если координатор падает между фазой 1 и фазой 2, участники зависают в «неопределённом» состоянии — они подготовились, но не знают, коммититься или откатываться. Они не могут снять блокировки до восстановления координатора. Восстановление может занять минуты.

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

Режим сбоя — не «транзакция иногда не проходит», а «вся система иногда зависает с удерживаемыми блокировками и неопределёнными участниками». Для высоконагруженного B2B биллинга это неприемлемо.

Why this works

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

Что такое сага

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

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

Ключевое свойство, которое саги не обеспечивают, — изоляция: промежуточные состояния видимы другим транзакциям. Если сага резервирует инвентарь на шаге 2 и затем падает на шаге 3 (платёж отклонён), между шагом 2 и компенсацией инвентарь выглядит зарезервированным для других читателей. Это структурное следствие использования локальных транзакций без распределённой блокировки.

Контрмеры против потери изоляции:

  • Семантические блокировки: помечать ресурсы как «в саге», чтобы другие транзакции считали их зарезервированными
  • Коммутативные обновления: проектировать обновления так, чтобы конечное состояние было одинаковым независимо от порядка, делая наблюдаемость промежуточного состояния безвредной
Викторина

Команда мигрирует с 2PC на саги для потока выполнения заказов. Разработчик говорит: «Саги дают нам ту же гарантию «всё или ничего», что и 2PC, только реализованную иначе». Старший инженер исправляет это утверждение. В чём точное различие?

B2B сага заказа: шаги и компенсирующие действия

Сага размещения заказа биллинговой платформы:

  1. OrderCreatedOrderService создаёт запись заказа локально, статус: pending. Компенсация: отменить заказ (пометить как cancelled).
  2. ReserveCreditPaymentService резервирует кредитный лимит клиента на сумму заказа. Компенсация: освободить резервацию кредита.
  3. ReserveInventoryInventoryService резервирует необходимый товар. Компенсация: освободить резервацию инвентаря.
  4. ScheduleShipmentShippingService планирует доставку. Компенсация: отменить запланированную отправку.

Если ScheduleShipment падает (например, нет перевозчика для адреса), сага запускает компенсацию в обратном порядке: отменить запланированную отправку (уже не удалась), освободить резервацию инвентаря, освободить резервацию кредита, отменить заказ.

Викторина

Сага биллинговой платформы падает на шаге ScheduleShipment после успешного коммита ReserveCredit и ReserveInventory. Реализация компенсации команды помечает сагу как 'failed' в журнале саги, но не выполняет никаких компенсирующих действий, полагая, что резервации инвентаря и кредита истекут автоматически через 24 часа через scheduled job. Что не так с таким подходом?

Оркестрированные vs хореографированные саги

Паттерн саги может быть реализован в любом из стилей координации из урока 01:

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

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

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

Викторина

Новый инженер приходит в биллинговую команду и спрашивает: «Почему мы не можем просто обернуть размещение заказа в транзакцию базы данных через OrderService, PaymentService и InventoryService? Мы уже везде используем PostgreSQL». Каков структурный ответ?

Вспомните перед уходом
  1. 01
    Что такое компенсирующее действие в саге и почему оно не то же самое, что откат базы данных?
  2. 02
    Почему саги не обеспечивают изоляцию и каковы два основных контрмера?
  3. 03
    Каково главное операционное преимущество хореографированной саги перед оркестрированной и каков её главный недостаток?
Итог

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

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

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

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

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 4 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

Примени это

Примени этот урок в реальном проекте.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.