Реализация Saga Pattern для распределённых транзакций
Представьте интернет-магазин с сотнями тысяч заказов в день. После успешной оплаты сервис доставки падает, и данные расходятся: деньги списаны, но заказ не отправлен. Без правильного управления распределёнными транзакциями такие сценарии ведут к финансовым потерям и оттоку клиентов. Saga Pattern решает эту проблему, разбивая бизнес-транзакцию на локальные шаги с компенсациями при сбоях. Мы более 5 лет реализуем саги в микросервисных системах — закажите решение под ключ с гарантией целостности данных.
Два вида Saga
Choreography (хореография) — сервисы реагируют на события друг друга без центрального координатора. Каждый сервис публикует события в брокер (например, Kafka) и подписывается на нужные. Если оплата не прошла, Inventory Service откатывает резерв по обратному событию.
Orchestration (оркестрация) — центральный Saga Orchestrator (например, Temporal) явно управляет шагами и компенсациями. Код понятнее, проще отлаживать, но требуется отдельный сервис.
| Критерий | Оркестрация | Хореография |
|---|---|---|
| Координация | Центральный координатор | Событийная шина (Kafka, RabbitMQ) |
| Сложность разработки | Средняя (нужен сервис-координатор) | Низкая на старте, высокая при многих сервисах |
| Отладка | Легко (логи координатора) | Сложно (трассировка событий) |
| Надёжность | Зависит от координатора | Децентрализована |
| Производительность | Один шаг за раз | Параллельные шаги (хуже контроль) |
Как выбрать между оркестрацией и хореографией?
Если у вас до 5 сервисов и важна простота отладки — выбирайте оркестрацию. Для крупных систем с 10+ сервисами и высокой нагрузкой хореография через Kafka даёт лучшую масштабируемость. Мы часто комбинируем: оркестрация для критичных цепочек, хореография для фоновых процессов. Наши инженеры подберут вариант под ваш проект — пишите, получите консультацию.
Что такое Temporal и зачем он нужен?
Temporal — production-ready движок для долгоживущих workflow. Он автоматически retry activities, сохраняет историю выполнения и позволяет инспектировать саги через UI. Гарантирует выполнение компенсаций даже при падении сервиса. Более 95% саг с Temporal завершаются без ручного вмешательства. По нашим данным, оркестрация через Temporal в 2-3 раза надёжнее хореографии без координатора. Пример оркестрации с Temporal:
import { proxyActivities, sleep } from '@temporalio/workflow'; const { reserveStock, chargePayment, createShipment, releaseStock, refund } = proxyActivities({ startToCloseTimeout: '10 seconds' }); export async function createOrderWorkflow(input: CreateOrderInput): Promise<void> { let stockReserved = false; let paymentCharged = false; try { await reserveStock({ orderId: input.orderId, items: input.items }); stockReserved = true; await chargePayment({ orderId: input.orderId, amount: input.amount }); paymentCharged = true; await createShipment({ orderId: input.orderId, address: input.address }); } catch (error) { // Temporal гарантирует выполнение компенсаций if (paymentCharged) { await refund({ orderId: input.orderId }); } if (stockReserved) { await releaseStock({ orderId: input.orderId }); } throw error; } } Персистентная Saga с состоянием
Сага должна переживать рестарты сервиса. Состояние хранится в БД (PostgreSQL, MySQL). Мы используем таблицу со статусами (running, completed, failed, compensating) и контекстом. При падении сервис перечитывает незавершённые саги и продолжает с последнего шага.
interface SagaState { sagaId: string; sagaType: string; status: 'running' | 'completed' | 'failed' | 'compensating'; currentStep: number; context: Record<string, unknown>; completedSteps: string[]; failedStep?: string; createdAt: Date; updatedAt: Date; } class PersistentSagaOrchestrator { async startSaga(sagaType: string, context: unknown): Promise<string> { const sagaId = uuidv4(); await this.sagaRepo.save({ sagaId, sagaType, status: 'running', currentStep: 0, context, completedSteps: [] }); await this.executeSaga(sagaId); return sagaId; } } Хореография через Kafka
Пример обработки событий в сервисе Inventory:
// Order Service публикует событие await kafka.producer.send({ topic: 'order.events', messages: [{ key: orderId, value: JSON.stringify({ type: 'OrderCreated', orderId, items, customerId })}] }); // Inventory Service слушает и резервирует kafka.consumer.subscribe({ topic: 'order.events' }); kafka.consumer.run({ eachMessage: async ({ message }) => { const event = JSON.parse(message.value.toString()); if (event.type !== 'OrderCreated') return; try { await inventoryService.reserveStock(event.orderId, event.items); await kafka.producer.send({ topic: 'inventory.events', messages: [{ key: event.orderId, value: JSON.stringify({ type: 'StockReserved', orderId: event.orderId })}] }); } catch { await kafka.producer.send({ topic: 'inventory.events', messages: [{ key: event.orderId, value: JSON.stringify({ type: 'StockReservationFailed', orderId: event.orderId })}] }); } } }); Типичные проблемы и их решения
| Проблема | Решение |
|---|---|
| Неидемпотентные операции | Проверка существующего состояния перед созданием (пример выше) |
| Потеря состояния саги при падении | Персистентное хранение статуса и контекста в БД |
| Бесконечные ретраи и перегрузка системы | Exponential backoff и лимит попыток (обычно 3-5) |
| Отсутствие мониторинга | Инструменты вроде Jaeger, Grafana для визуализации хода саги |
Что входит в работу
Процесс реализации Saga Pattern включает этапы:
- Анализ: определение бизнес-транзакций, границ сервисов, точек отказа.
- Проектирование: выбор между оркестрацией и хореографией, подготовка схемы компенсаций.
- Реализация: написание кода саг, интеграция с Temporal или Kafka, обеспечение идемпотентности.
- Тестирование: unit-тесты, интеграционные тесты, тесты на сценарии сбоев (chaos engineering).
- Деплой: развёртывание в Docker/Kubernetes, настройка мониторинга (Jaeger, Grafana).
Результат: документация саг, доступ к репозиторию, обучение вашей команды и поддержка 2 недели после запуска.
Пример сложной саги с несколькими компенсациями
В реальных проектах одна сага может объединять десятки сервисов. Например, при заказе с предзаказом и доставкой: резервирование на складе, оплата, создание заказа у поставщика, оформление доставки. Компенсации запускаются в обратном порядке, а Temporal гарантирует выполнение даже после нескольких перезапусков.
Сроки и гарантии
- Простая оркестрация (2-3 сервиса, без Temporal) — от 1 до 2 недель.
- Оркестрация с Temporal + мониторинг — от 2 до 3 недель.
- Хореография через Kafka с идемпотентными обработчиками — от 2 до 4 недель.
Стоимость рассчитывается индивидуально после анализа. Мы даём гарантию на код 6 месяцев. Более 100 реализованных проектов в микросервисах (5+ лет на рынке). Пишите — оценим ваш проект бесплатно, предложим оптимальное решение.
Дополнительные материалы: Saga pattern (Wikipedia).







