Transactional outbox для БД и Kafka
Что делать, если бизнес-изменение записалось в БД, а событие в Kafka не отправилось?
Ответить самому
Сначала сформулируйте ответ как на собеседовании, затем откройте разбор и оцените себя.
Короткий ответ
В одной транзакции с бизнес-изменением записать событие в outbox-таблицу. Отдельный worker читает pending events, отправляет их в Kafka и помечает как sent с retry.
Полный разбор
Проблема dual write: БД и Kafka не участвуют в одной простой транзакции. Если сначала обновить БД, а потом отправить event, между этими шагами процесс может упасть. Если сначала отправить event, а потом не записать БД, потребители увидят событие про несуществующее состояние.
Transactional outbox решает это так: бизнес-изменение и запись event в outbox-таблицу происходят в одной DB transaction. После commit отдельный worker или CDC-процесс читает outbox, публикует события в Kafka и помечает их как sent. При сбоях он ретраит.
Потребителям все равно нужна идемпотентность, потому что delivery обычно at-least-once. Для порядка событий используют aggregate id, sequence/version и partitioning key.
Теория
Outbox переносит атомарность туда, где она есть: в транзакцию одной базы данных.
Типичные ошибки
- Делать два независимых write без recovery-механизма.
- Не делать consumers идемпотентными.
- Не хранить статус и retry count outbox-события.