Поскольку SAGA не блокирует базу данных глобально (как ACID-транзакция), в ней возникают специфические проблемы, которые нужно обрабатывать на уровне кода.
1. Аномалии данных
- Lost Updates (Потерянные обновления): Две саги одновременно меняют один объект, одна перезаписывает изменения другой.
- Dirty Reads (Грязные чтения): Одна сага читает данные, которые другая собирается откатывать.
Решение: Использование оптимистичных блокировок с версионированием.
# Пример версионирования для избежания lost updates
def update_user_balance(user_id, amount, expected_version):
user = get_user_with_version(user_id)
if user.version != expected_version:
raise OptimisticLockException("User data was modified by another process")
user.balance += amount
user.version += 1
save_user(user)
2. Зависшие саги и Тайм-ауты
Что делать, если одна из Retryable транзакций так и не завершилась успешно? Сеть лежит, стороннее API недоступно.
В прошлом уроке уже затрагивали момент с повторами.
После N попыток сервис должен остановиться. Зависшая Сага записывается в отдельную БД или очередь (часто называют Dead Letter Queue), и срабатывает алерт. Дальше инцидент разбирают разработчики или поддержка вручную.
class SagaTimeoutHandler:
def __init__(self, max_retries=3, timeout_minutes=30):
self.max_retries = max_retries
self.timeout_minutes = timeout_minutes
async def handle_stuck_saga(self, saga_id):
saga = await self.get_saga_state(saga_id)
if saga.retries >= self.max_retries:
# Лимит исчерпан: Переводим в ручную обработку (DLQ)
await self.send_to_manual_processing(saga)
await self.notify_ops_team(saga)
else:
# Пробуем еще раз, не забывая про ИДЕМПОТЕНТНОСТЬ!
await self.retry_saga_step(saga)
Как это реализуют в BigTech?
Строить Оркестратор для SAGA с нуля, обрабатывая все таймауты, оптимистичные блокировки и ретраи вручную — гиблое дело. Яндекс, T-Bank и другие гиганты используют готовые фреймворки. Современный стандарт индустрии — Temporal.io.
- Как выбрать между Хореографией и Оркестрацией? Отличный разбор от инженеров Temporal.
- Как правильно проектировать компенсирующие действия? Подробная статья с примерами кода.