TGViewer
Java Portal | Программирование Java Portal | Программирование @java_iibrary · 11.6K subscribers
Post #1982 2.35K
Одна из самых опасных проблем в распределенных системах это двойная запись:

сохранить что-то в своей базе и параллельно опубликовать событие в другой системе, рассчитывая что оба шага пройдут вместе.

А что если нет?

Представь классический поток:

1️⃣Сохраняешь заказ в базе.
2️⃣Отправляешь событие в Kafka или делаешь запрос в другую API, чтобы сообщить что заказ создан.

Где проблема?

👉Если шаг 1 прошел, а шаг 2 упал:
База говорит заказ создан, но внешний сервис об этом не знает.

👉Если шаг 2 прошел, а шаг 1 упал:
Ты опубликовал фантомное событие о том, чего нет.

Это и есть двойная запись.

И если думаешь что это тебя не коснется, просто подожди пока прод покажет тебе реальность.

Тут и появляется Transactional Outbox Pattern.

Что именно делает этот паттерн? 🤔

Избавляет от необходимости писать в два места одновременно.
Превращает внешнюю запись в надежный процесс.

Идея простая, вот минимальный способ это реализовать:

1. Когда сохраняешь данные в базе (INSERT/UPDATE),
ты параллельно пишешь событие в отдельную таблицу, например outbox.

2. Оба INSERT выполняются в одной транзакции.
Если что-то падает, падает все.
Так достигается гарантированное консистентное состояние.

3. Затем отдельный процесс (poller или scheduler) читает эту таблицу и публикует реальное событие в Kafka, RabbitMQ или куда нужно.

4. Если публикация упала, ничего страшного.
Событие остается в таблице пока его не получится отправить.

С этим достаточно простым потоком ты получаешь консистентность без двойной записи.

Почему это так хорошо работает?

Потому что принимает неприятную правду:

Нельзя рассчитывать что два разных системы корректно обработают одну и ту же транзакцию.

❌База умеет в транзакции.
❌Kafka — нет.
❌Rabbit — нет.
❌Webhook тем более.

Поэтому решение не в том чтобы это «продавить», а в том чтобы адаптировать архитектуру к реальности:

Единственная запись, которой реально можно доверять — запись в твою базу.

Все внешнее (то что не хранится в твоей базе) должно выполняться позже, с ретраями, логами и прочим.

Но да, это не бесплатно.

Нужно:

—> Создать таблицу outbox.
—> Настроить ретраи.
—> Удалять обработанные события.
—> Мониторить poller (или любую другую реализацию).
—> Не допустить двойные вставки в outbox чтобы избежать дублей.

И ключевая мысль:

Цена Transactional Outbox намного ниже цены ручной починки рассинхрона между сервисами и системами.

А это в проде дороже золота.

👉 Java Portal
  • 👍 9
  • ❤ 3
More from @java_iibrary
  1. Sep 28, 2026Проблема в продакшене. Приложение зависло. Вы запускаете: jstack <pid> Через несколько сек…
  2. Sep 27, 2026Java-разработчики, CopyOnWriteArrayList создаёт копию всего внутреннего массива при каждом…
  3. Sep 27, 2026Java-разработчики, ConcurrentHashMap потокобезопасен. Но он не блокирует всю коллекцию при…
  4. Sep 26, 2026Во многих приложениях есть такой эндпоинт. Фронтенд удаляет JWT после того, как пользовате…
  5. Sep 26, 2026Java: используйте Deque вместо Stack для работы по принципу LIFO («последним пришёл — перв…
  6. Sep 25, 2026Каждый Java-разработчик использует HashMap. Но задумывались ли вы… Почему его ёмкость всег…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →