Транспорт бэкенда
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?