Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Паттерн SAGA
SAGA — это архитектурный паттерн для управления транзакциями в распределённых системах, особенно в микросервисной архитектуре. SAGA решает проблему поддержания консистентности данных когда операция охватывает несколько сервисов.
Проблема ACID транзакций в микросервисах
В монолитном приложении можно использовать традиционные ACID транзакции БД. Но в микросервисной архитектуре каждый сервис имеет свою БД, и ACID транзакции через сетевые границы невозможны:
# Проблема: как гарантировать консистентность?
# Операция: заказ платежа + списание денег + резервирование товара
# Каждая часть в разных сервисах!
order_service.create_order() # Сервис 1
payment_service.charge() # Сервис 2
inventory_service.reserve() # Сервис 3
Если payment_service не ответит, как откатить изменения?
Два типа SAGA
1. Хореография (Choreography)
Каждый сервис слушает события других сервисов и самостоятельно инициирует следующий шаг. Сервисы общаются через события (message broker или event bus):
# Order Service
def create_order(order_data):
order = Order.create(**order_data)
event_bus.publish("OrderCreated", {"order_id": order.id, "amount": order.total})
return order
# Payment Service (слушает OrderCreated)
@event_bus.subscribe("OrderCreated")
def handle_order_created(event):
try:
transaction = charge_payment(event["amount"])
event_bus.publish("PaymentProcessed", {"order_id": event["order_id"]})
except PaymentError:
event_bus.publish("PaymentFailed", {"order_id": event["order_id"]})
# Inventory Service (слушает PaymentProcessed)
@event_bus.subscribe("PaymentProcessed")
def handle_payment_processed(event):
try:
reserve_items(event["order_id"])
event_bus.publish("OrderCompleted", {"order_id": event["order_id"]})
except InventoryError:
event_bus.publish("OrderFailed", {"order_id": event["order_id"]})
# Может потребоваться компенсирующая транзакция
Преимущества хореографии:
- Слабая связанность между сервисами
- Простая для малых систем
Недостатки:
- Сложно отследить и отладить процесс
- Циклические зависимости событий
- Жёсткая связь через события
2. Оркестрация (Orchestration)
Центральный оркестратор (Saga Orchestrator) управляет последовательностью вызовов и знает о каждом шаге:
from dataclasses import dataclass
from enum import Enum
class SagaStep(Enum):
ORDER_CREATED = "order_created"
PAYMENT_PROCESSING = "payment_processing"
INVENTORY_RESERVED = "inventory_reserved"
COMPLETED = "completed"
FAILED = "failed"
@dataclass
class Order:
id: str
amount: float
status: SagaStep
compensations: list # Для отката
class OrderSagaOrchestrator:
def __init__(self, order_service, payment_service, inventory_service):
self.order_service = order_service
self.payment_service = payment_service
self.inventory_service = inventory_service
def execute_saga(self, order_data):
order = Order(
id=order_data["id"],
amount=order_data["amount"],
status=SagaStep.ORDER_CREATED,
compensations=[]
)
try:
# Шаг 1: создаём заказ
self.order_service.create_order(order.id, order.amount)
order.compensations.append(("cancel_order", order.id))
# Шаг 2: списываем платёж
transaction_id = self.payment_service.charge(order.amount)
order.compensations.append(("refund_payment", transaction_id))
# Шаг 3: резервируем товар
self.inventory_service.reserve(order.id)
order.status = SagaStep.COMPLETED
return order
except Exception as e:
# Откатываем в обратном порядке (компенсирующие транзакции)
order.status = SagaStep.FAILED
self.rollback_saga(order)
raise
def rollback_saga(self, order):
"""Откатываем состояние через компенсирующие транзакции"""
for compensation_type, *args in reversed(order.compensations):
if compensation_type == "cancel_order":
self.order_service.cancel_order(args[0])
elif compensation_type == "refund_payment":
self.payment_service.refund(args[0])
Преимущества оркестрации:
- Видна вся логика в одном месте
- Легче отследить и отладить
- Явная обработка ошибок
Недостатки:
- Оркестратор становится single point of failure
- Более тесная связанность
- Оркестратор требует заботы о его состоянии
Компенсирующие транзакции
Ключевой концепт SAGA — компенсирующие транзакции. Если что-то не удалось, мы не откатываем БД, а выполняем противоположные операции:
# Вместо откатки платежа...
actual_transaction = payment_service.charge(100)
# ...выполняем компенсирующую транзакцию
if inventory_error:
payment_service.refund(actual_transaction.id)
Когда использовать SAGA
- Микросервисная архитектура
- Процессы, охватывающие несколько сервисов
- Когда 2PC (Two-Phase Commit) недоступен
- Требуется высокая доступность
Паттерн SAGA — это отличное решение для поддержания консистентности в распределённых системах без полагания на классические ACID транзакции.