Какой у вас опыт применения паттерна Saga?

Ответ

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

Два основных стиля реализации:

  1. Оркестрация (Orchestration): Центральный координатор (оркестратор) управляет потоком выполнения.

    // Упрощенный пример оркестратора Saga
    public class OrderCreationSagaOrchestrator {
        public void execute(Order order) {
            try {
                inventoryService.reserveItems(order); // Шаг 1
                paymentService.charge(order);         // Шаг 2
                shippingService.schedule(order);      // Шаг 3
                // Saga завершена успешно
            } catch (Exception e) {
                // Запуск компенсирующих действий
                paymentService.refund(order);
                inventoryService.releaseItems(order);
                // Saga завершена с ошибкой
            }
        }
    }
  2. Хореография (Choreography): Сервисы координируют работу через обмен событиями без центрального управляющего компонента.

Критически важные аспекты при реализации:

  • Идемпотентность: Все шаги и компенсации должны быть идемпотентными для безопасного повтора.
  • Наблюдаемость: Необходимо сквозное логирование (correlation ID) для отслеживания состояния Saga.
  • Сложность отладки: Распределенный характер Saga усложняет отладку по сравнению с монолитной транзакцией.

Опыт применения: Использовал Saga с оркестрацией (на Spring State Machine) для реализации процесса оформления заказа в e-commerce, где необходимо согласованно обновить инвентарь, списать деньги и создать доставку.

Ответ 18+ 🔞

Ну вот, опять про эти ваши саги, блядь. Сидишь такой, думаешь — микросервисы, красота, независимость, масштабирование, ёпта! А потом бац — и надо заказ оформить, который в трёх разных конторах данные трогает. И тут тебе классические транзакции на базу данных, как манна небесная, уже не светят, потому что каждый сервис свою отдельную базу имеет, сука. И начинается этот цирк.

Saga — это, грубо говоря, такой костыль, но охуенно продуманный, для вот этих самых долгих, блядь, бизнес-процессов. Вместо того чтобы всё сделать разом и откатить разом (как в старые добрые ACID-времена), мы эту операцию на куски режем. Каждый кусок — это локальный шаг в одном сервисе. Сделал шаг — выпустил событие, чтобы следующий за тобой подхватил. А если где-то посередине пиздец случился, то надо откатывать всё, что уже наворотили. Но не через волшебный ROLLBACK, а вручную, сука, через компенсирующие транзакции. То есть для каждого шага пишешь ещё и обратную операцию, которая всё по-братски вернёт как было. Веселье, блядь, ещё то.

Как это, блядь, можно организовать? Да как обычно — или бардак, или диктатура.

  1. Оркестрация (Orchestration). Это когда есть один главный по тарелочкам, оркестратор. Он, как мамка непутёвым детям, всем командует: ты сделай это, ты — то, а ты вообще стой, блядь, не двигайся. Все шаги идут строго по его указке. Если где-то сбой — он же и запускает откат по составленному им же плану.

    // Вот смотри, типа как этот зануда-оркестратор может выглядеть
    public class OrderCreationSagaOrchestrator {
        public void execute(Order order) {
            try {
                inventoryService.reserveItems(order); // Шаг 1: резервируем товар
                paymentService.charge(order);         // Шаг 2: списываем бабки
                shippingService.schedule(order);      // Шаг 3: планируем доставку
                // Всё, сага успешно завершена, можно выдохнуть
            } catch (Exception e) {
                // А вот тут пиздец, чувак! Начинаем откат, как в боевике.
                paymentService.refund(order);         // Компенсация: возвращаем бабки
                inventoryService.releaseItems(order); // Компенсация: разрезервируем товар
                // Всё, сага похерена, но система в консистентном состоянии. Вроде как.
            }
        }
    }
  2. Хореография (Choreography). А это уже анархия, блядь. Нет центральной власти. Каждый сервис, сделав своё дело, кричит в общую шину событий: «Эй, я тут товар зарезервировал!». Следующий сервис этот крик слышит, делает своё дело и кричит: «А я бабки списал!». И так по цепочке. Если кто-то нихуя не сделал и не прокричал — все остальные так и будут ждать, как идиоты. А для отката нужно, чтобы каждый сервис ещё и умел слушать события о пиздеце и запускать свою компенсацию. Бардак, но зато распределённый, ебать его в сраку.

А теперь, блядь, самое важное, что все просрать могут:

  • Идемпотентность — это святое. Твои шаги и компенсации могут вызвать по три раза из-за таймаутов, повторов и прочей сетевой хуйни. Они должны быть готовы к этому. Вызвали charge(order) дважды? Должно быть всё равно, что один раз. Иначе на клиенте двойное списание, а он тебе потом ебальник набьёт.
  • Наблюдаемость. Ты должен всегда знать, в каком состоянии твоя сага, а то она где-то посередине зависнет, и хуй поймёшь — деньги списались или нет, товар зарезервирован или уже нет. Correlation ID — твой лучший друг, прокидывай его везде и логируй по нему.
  • Сложность отладки. Отлаживать такую распределённую хуйню — это тот ещё пиздец. В монолите ты в дебаггере прошёлся по стеку и всё понял. А тут у тебя логи по десяти сервисам размазаны, события где-то потерялись... Ёперный театр, короче.

Опыт применения: Я эту банду настраивал в одном интернет-магазине, на Spring State Machine, по схеме с оркестратором. Ну, потому что хореография — это для мазохистов, когда сервисов три и все написаны тобой же. А тут процесс оформления заказа: резерв на складе, оплата, создание доставки. Оркестратор рулил этим процессом как царь. Работало, но, блядь, сколько крови это выпило на этапе отладки и обеспечения той самой идемпотентности... Волосы дыбом вставали, ей-богу.