Что такое паттерн Saga и как он управляет транзакциями в микросервисной архитектуре?

Ответ

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

Основная идея:

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

Два подхода к реализации:

  1. Хореография (Choreography): Децентрализованный подход. Каждый сервис после выполнения своей локальной транзакции публикует событие. Другие сервисы подписываются на эти события и запускают свои локальные транзакции. Компенсация также инициируется событиями.
  2. Оркестрация (Orchestration): Централизованный подход. Существует специальный сервис-оркестратор, который управляет всем процессом: он вызывает нужные сервисы в нужной последовательности и запускает компенсирующие транзакции в случае сбоя.

Пример: Оркестрация процесса заказа

Представим процесс оформления заказа, который затрагивает сервисы Orders, Payments и Inventory.

# Псевдокод для сервиса-оркестратора

class OrderSaga:
    def execute_order(self, order_details):
        executed_steps = []
        try:
            # Шаг 1: Создать заказ
            order_id = orders_service.create_order(order_details)
            executed_steps.append(self.compensate_create_order)

            # Шаг 2: Списать средства
            payments_service.process_payment(order_id, order_details.cost)
            executed_steps.append(self.compensate_process_payment)

            # Шаг 3: Зарезервировать товар на складе
            inventory_service.reserve_items(order_id, order_details.items)
            executed_steps.append(self.compensate_reserve_items)

            print("Сага успешно завершена!")

        except Exception as e:
            print(f"Ошибка в саге: {e}. Запуск компенсации...")
            self.rollback(executed_steps)

    def rollback(self, executed_steps):
        # Компенсирующие транзакции вызываются в обратном порядке
        for step in reversed(executed_steps):
            step()

    # Компенсирующие методы
    def compensate_create_order(self, ...):
        orders_service.cancel_order(...)

    def compensate_process_payment(self, ...):
        payments_service.refund_payment(...)

    def compensate_reserve_items(self, ...):
        inventory_service.release_items(...)

Преимущества:

  • Сохраняет слабую связанность сервисов.
  • Повышает доступность системы, так как не использует долговременные блокировки ресурсов.

Недостатки:

  • Усложняет логику приложения из-за необходимости реализации компенсирующих транзакций.
  • Усложняет отладку и тестирование распределенного процесса.

Ответ 18+ 🔞

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

Суть, если по-простому:

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

Как это можно устроить? Да двумя способами, хуле.

  1. Хореография (Choreography): Это когда все сервисы, как мартышки на банановой плантации, начинают суетиться сами, услышав нужный сигнал. Один сделал своё дело — крикнул "Ёпта, я готов!" (опубликовал событие). Другой это событие услышал, сделал своё, и тоже крикнул. Если кто-то накосячил, он кричит "Всё пиздец!" и все начинают откатывать кто что успел. Хаос, а не система. Но зато без единого начальника.
  2. Оркестрация (Orchestration): А вот это уже поинтереснее. Тут появляется сервис-оркестратор, этакий главный по тарелочкам. Он, блядь, всем командует: "Ты, сервис заказов, делай раз! Ты, сервис оплат, делай два!". И если на шаге "делай два" вылезла ошибка, оркестратор такой: "Ах ты ж, пидарас шерстяной... Ну всё, отмена! Сервис заказов, отменяй раз!". Всё централизовано и, в теории, под контролем.

Вот смотри, как это может выглядеть в коде, если брать оркестрацию для заказа:

# Псевдокод для сервиса-оркестратора

class OrderSaga:
    def execute_order(self, order_details):
        executed_steps = []
        try:
            # Шаг 1: Создать заказ
            order_id = orders_service.create_order(order_details)
            executed_steps.append(self.compensate_create_order)

            # Шаг 2: Списать средства
            payments_service.process_payment(order_id, order_details.cost)
            executed_steps.append(self.compensate_process_payment)

            # Шаг 3: Зарезервировать товар на складе
            inventory_service.reserve_items(order_id, order_details.items)
            executed_steps.append(self.compensate_reserve_items)

            print("Сага успешно завершена!")

        except Exception as e:
            print(f"Ошибка в саге: {e}. Запуск компенсации...")
            self.rollback(executed_steps)

    def rollback(self, executed_steps):
        # Компенсирующие транзакции вызываются в обратном порядке
        for step in reversed(executed_steps):
            step()

    # Компенсирующие методы
    def compensate_create_order(self, ...):
        orders_service.cancel_order(...)

    def compensate_process_payment(self, ...):
        payments_service.refund_payment(...)

    def compensate_reserve_items(self, ...):
        inventory_service.release_items(...)

Что тут хорошо, спросишь?

  • Сервисы не привязаны друг к другу намертво, живут своей жизнью.
  • Система не встаёт колом из-за долгих блокировок, всё асинхронно и бодренько.

А что пиздецово?

  • Логика, блядь, усложняется в три раза. Тебе теперь надо не только сделать действие, но и придумать, как его отменить. А отмена — это не всегда просто "удалить запись".
  • Попробуй это всё отладить, когда процесс размазан по пяти сервисам, а компенсация может сама упасть. Удивление пиздец, одним словом. Волнение ебать, терпения ноль ебать.