Что такое Saga Pattern и какие его виды вы знаете?

Ответ

Saga — это паттерн управления распределёнными транзакциями, который помогает поддерживать консистентность данных в микросервисной архитектуре без использования двухфазных коммитов (2PC).

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

Существует два основных подхода к координации саги:

1. Хореография (Choreography)

В этом подходе нет центрального координатора. Каждый сервис после выполнения своей транзакции публикует событие в общую шину (например, Kafka или RabbitMQ). Другие сервисы подписываются на эти события и реагируют на них, запуская свои локальные транзакции.

  • Плюсы: Децентрализация (нет единой точки отказа), слабая связанность сервисов.
  • Минусы: Сложно отслеживать и отлаживать весь процесс, так как логика распределена по многим сервисам. Возможен риск циклических зависимостей между событиями.

Пример логики: Сервис Заказов создает заказ и публикует событие OrderCreated. Сервис Оплаты слушает это событие, списывает средства и публикует PaymentCompleted.

2. Оркестрация (Orchestration)

В этом подходе существует центральный сервис-координатор (оркестратор), который управляет всем процессом. Он отправляет команды сервисам-участникам и ожидает от них ответа. Оркестратор знает всю последовательность шагов и отвечает за запуск компенсирующих транзакций в случае сбоя.

  • Плюсы: Централизованная логика, которую легко понять, отладить и изменить. Проще управлять процессом компенсации.
  • Минусы: Оркестратор становится единой точкой отказа и потенциальным узким местом. Сервисы становятся более связанными с оркестратором.

Пример логики:

// Псевдокод оркестратора
func ExecuteOrderSaga(orderID string) error {
    if err := orderService.Create(orderID); err != nil {
        return err // Начальный шаг не удался
    }
    if err := paymentService.Process(orderID); err != nil {
        // Запуск компенсирующей транзакции
        orderService.Cancel(orderID)
        return err
    }
    if err := deliveryService.Ship(orderID); err != nil {
        // Запуск двух компенсирующих транзакций
        paymentService.Refund(orderID)
        orderService.Cancel(orderID)
        return err
    }
    return nil
}
Критерий Хореография Оркестрация
Координация Децентрализованная (через события) Централизованная (сервис-оркестратор)
Связанность Низкая Высокая (с оркестратором)
Отладка Сложная Относительно простая
Точка отказа Нет единой Оркестратор