В микросервисной архитектуре заказ затрагивает сервисы оплаты, склада и доставки. Как обеспечить согласованность без распределённых транзакций? Расскажите про saga и transactional outbox.
Короткий ответ
- 2PC в микросервисах не используют: блокировки и хрупкость
- Saga — цепочка локальных транзакций с компенсациями при сбое
- Хореография через события или оркестрация центральным координатором
- Компенсация — бизнес-отмена, а не rollback базы
- Outbox: событие пишется в ту же локальную транзакцию, что и данные
- Релей публикует события из outbox в брокер — ничего не теряется
- Потребители идемпотентны: доставка at-least-once даёт дубли
Saga разбивает бизнес-процесс на локальные транзакции с компенсациями, а outbox гарантирует, что изменение данных и публикация события происходят атомарно.
Как сказать вслух
пример ответаЯ объясню, почему двухфазный коммит здесь не вариант, и предложу сагу: каждый сервис делает свою локальную транзакцию и публикует событие, а при сбое выполняются компенсации в обратном порядке. Отдельно покажу проблему двойной записи — в базу и в брокер — и закрою её паттерном transactional outbox.
Подробный ответ
Основной ответ
Распределённая транзакция через 2PC держит блокировки на время координации и падает вместе с координатором, поэтому в микросервисах процесс оформляют сагой: заказ создан → оплата списана → товар зарезервирован → доставка назначена. Каждый шаг — локальная ACID-транзакция своего сервиса. При сбое шага выполняются компенсирующие действия в обратном порядке: вернуть деньги, снять резерв, отменить заказ. Координация — хореография (сервисы реагируют на события друг друга, просто, но процесс размазан) или оркестрация (отдельный компонент ведёт состояние саги — наглядно для длинных процессов). Публикация событий надёжна через outbox: сервис в одной транзакции пишет данные и строку события в таблицу outbox, затем релей (поллер или CDC типа Debezium) отправляет её в Kafka. Потребители дедуплицируют по id события.
Ключевые моменты
- Компенсация — не rollback. Деньги уже списаны, письмо отправлено; компенсация — это новая бизнес-операция «возврат», и некоторые действия некомпенсируемы — их ставят последним шагом.
- Хореография vs оркестрация. Хореография хороша для 2-3 шагов; при длинных процессах оркестратор даёт видимое состояние, таймауты и ретраи в одном месте.
- Проблема двойной записи. Записать в БД и отдельно послать в брокер нельзя атомарно: между ними сервис может упасть; outbox сводит обе записи в одну локальную транзакцию.
- Идемпотентные потребители. Релей гарантирует at-least-once, значит дубли неизбежны; потребитель хранит обработанные id событий или делает обработку естественно идемпотентной.
Практический контекст
Интервьюер смотрит, понимаете ли вы цену eventual consistency: статусы «в обработке» в UI, обработку частичных отказов, некомпенсируемые шаги. Полезно привязать к DDD: граница саги обычно совпадает с границами агрегатов и bounded context — внутри агрегата транзакция, между ними события. Уточните: сколько шагов в процессе, есть ли шаги без отмены, какой допустимый интервал рассинхронизации для бизнеса.
Частые ошибки
- Предлагают двухфазный коммит между сервисами, игнорируя блокировки и отказ координатора
- Пишут в базу и публикуют в Kafka двумя отдельными операциями — при падении между ними событие теряется
- Считают компенсацию откатом транзакции и не продумывают некомпенсируемые шаги вроде отправленного email