⚡ Outbox-паттерн: спасение от потерянных событий (и денег)
В одном из первых постов я рассказывал, как из-за моей ошибки контора потеряла 10к $. Тогда я отправлял события в очередь до записи в БД. Запрос к БД упал, а уведомление уже улетело в платежный сервис. В итоге деньги списали, но не зафиксировали в системе, а потом это повторилось еще много-много раз.
Если бы тогда использовали реляционную БД и Outbox-паттерн, этого бы не случилось.
🤔 В чем проблема?
Типичная схема взаимодействия с брокером:
1 Пишем данные в БД
2 Отправляем событие в очередь
Но что если брокер упал или вызов события не прошел после успешного коммита в БД? Система потеряет важное уведомление, а другой сервис никогда не узнает об изменениях.
Решения без Outbox:
🔹 Повторять отправку? — Дубликаты и сложность в обработке
🔹 Держать транзакцию до подтверждения от брокера? — Тормоза в БД
🔹 Игнорировать? — Потерянные данные и баги
🛠 Как решает Outbox?
Вместо того чтобы сразу пушить событие:
✅ Записываем его в специальную таблицу (outbox) в той же транзакции, что и изменение данных
✅ Отдельный процесс (consumer, cron-job, Debezium) читает outbox и отправляет события в брокер
✅ После успешной отправки удаляем запись из outbox
🎯 Почему это круто?
✔ Гарантия доставки — без потерь, даже если брокер лег
✔ Идемпотентность — события можно повторно обработать без дубликатов
✔ Разделение ответственности — бизнес-логика в одном месте, отправка в другом
🚀 Когда использовать?
✔ Если твои сервисы взаимодействуют через event-driven архитектуру
✔ Если тебе нужно гарантированно отправлять события, даже при сбоях
✔ Если ты используешь реляционную БД и хочешь избавиться от сложностей с distributed transactions
Если бы у нас тогда был Outbox + реляционная БД, я бы просто записал событие в outbox в одной транзакции с изменением данных. Даже если бы платежный сервис упал, событие никуда бы не пропало — оно бы отправилось при следующей обработке.
Вывод: если работаешь с реляционной БД и событиями — Outbox обязателен. Уже используешь или пока надеешься на удачу? Давай обсудим в комментах! 💬