Реализация 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).







