Транспорт бэкенда

Saga Pattern: распределённые транзакции

Stripe обрабатывает платёж. Одновременно: списать с карты, обновить баланс мерчанта, отправить webhook, обновить аналитику, начислить комиссию. Если на шаге 4 произошла ошибка - деньги уже списаны. Как откатить то, что уже случилось? Saga Pattern - ответ на этот вопрос без 2PC и блокировок.

  • **Stripe** использует Saga для обработки платежей: каждый шаг - локальная транзакция с компенсацией. При отказе на любом шаге - автоматический возврат.
  • **Uber** оркестрирует Trip через внутренний workflow engine: создание поездки, матчинг водителя, трекинг, оплата - всё как одна Saga с компенсациями при отмене.
  • **DoorDash** мигрировал на Temporal для управления заказами: доставка может занять часы, workflow живёт всё это время с автоматическими retry и эскалациями.

Проблема распределённых транзакций

В монолите ACID-транзакция гарантирует атомарность: либо всё, либо ничего. В микросервисах каждый сервис имеет свою БД - межсервисная транзакция невозможна стандартными средствами. 2PC (Two-Phase Commit) технически решает это, но блокирует ресурсы на время координации и делает систему недоступной при сбое координатора.

Hector Garcia-Molina и Kenneth Salem предложили Saga в 1987 году для долгоживущих транзакций. В эпоху микросервисов это стало стандартным паттерном для межсервисной координации.

Почему 2PC (Two-Phase Commit) редко используется в микросервисной архитектуре?

Choreography Saga

Хореография (Choreography) - каждый сервис знает, какое событие опубликовать и на какие события реагировать. Нет центрального координатора: сервисы общаются через события в брокере (Kafka, RabbitMQ). Loose coupling, но сложно отследить весь flow.

Хореография хороша для простых flows с 2-3 шагами. При 5+ шагах становится сложно ответить на вопрос 'в каком состоянии сейчас сага?' без дополнительного мониторинга.

Главный недостаток Choreography Saga при большом количестве шагов:

Orchestration Saga

Оркестрация (Orchestration) - центральный оркестратор (Saga Orchestrator) управляет каждым шагом: вызывает сервисы, получает ответы, решает что делать дальше. Весь flow виден в одном месте. Netflix, Uber, Stripe используют этот подход.

Оркестратор должен быть идемпотентным и персистентным: при рестарте он должен восстановить состояние и продолжить с последнего шага. Это решает Temporal.io: workflow код пишется как обычный код, а персистентность берёт на себя платформа.

Почему состояние саги нужно хранить в БД (persistently), а не только в памяти?

Компенсирующие транзакции

Компенсирующая транзакция (Compensating Transaction) отменяет бизнес-эффект уже завершённой локальной транзакции. Это не SQL ROLLBACK - данные уже committed. Компенсация создаёт новую запись, отменяющую предыдущую: возврат денег вместо отмены списания.

Если компенсация тоже падает - нужен механизм повторных попыток с backoff. В конечном счёте система либо успешно компенсирует, либо требует ручного вмешательства (Dead Letter Queue, алерт на дежурного).

Компенсирующая транзакция для списания денег с карты - это:

Temporal: durable workflow engine

Temporal.io - платформа для durable workflows: workflow код пишется как обычный код (TypeScript/Go/Java), а Temporal автоматически обеспечивает персистентность, retry, таймауты и восстановление после сбоев. Используется в Stripe, Netflix, DoorDash, Instacart.

Temporal хранит полную историю выполнения workflow в своём event store. При сбое воркера - новый воркер replay'ит историю и продолжает с последней точки. Workflow может длиться часы, дни, недели.

Orchestration Saga и Choreography Saga взаимоисключают друг друга

Их можно комбинировать: оркестратор управляет верхнеуровневым flow, внутри шагов - хореография через события

Реальные системы часто гибридны: Temporal оркестрирует основной flow заказа, а уведомления о статусах распространяются через Kafka (хореография). Критерий выбора - сложность flow и требования к наблюдаемости.

Как Temporal восстанавливает workflow после падения воркера?

Ключевые идеи

  • **Choreography vs Orchestration** - хореография через события (loose coupling, сложно дебажить), оркестрация через центральный coordinator (видно весь flow, единая точка контроля).
  • **Компенсации** - это бизнес-операции, не SQL ROLLBACK. Должны быть идемпотентными - система может вызвать их несколько раз при retry.
  • **Temporal** решает сложность оркестрации: workflow пишется как обычный код, платформа берёт на себя персистентность, retry и восстановление при сбоях.

Связанные темы

Saga Pattern работает поверх event-driven архитектуры и требует надёжной доставки:

  • Event-Driven Architecture — Choreography Saga строится на событиях - понимание EDA необходимо для реализации хореографии
  • Outbox Pattern и CDC — Outbox обеспечивает надёжную публикацию событий из Saga шагов - критично для at-least-once доставки

Вопросы для размышления

  • Как реализовать идемпотентность компенсирующей транзакции, если внешняя система (например, банк) не поддерживает idempotency keys?
  • При каком количестве шагов в саге стоит переходить от хореографии к оркестрации? Какие метрики помогут принять это решение?
  • Что произойдёт, если компенсирующая транзакция тоже упадёт? Как выстроить escalation policy?

Связанные уроки

  • dist-07-transactions
Saga Pattern: распределённые транзакции

0

1

Войти