Одна из самых опасных проблем в распределенных системах это двойная запись:
сохранить что-то в своей базе и параллельно опубликовать событие в другой системе, рассчитывая что оба шага пройдут вместе.
А что если нет?
Представь классический поток:
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
сохранить что-то в своей базе и параллельно опубликовать событие в другой системе, рассчитывая что оба шага пройдут вместе.
А что если нет?
Представь классический поток:
Где проблема?
База говорит заказ создан, но внешний сервис об этом не знает.
Ты опубликовал фантомное событие о том, чего нет.
Это и есть двойная запись.
И если думаешь что это тебя не коснется, просто подожди пока прод покажет тебе реальность.
Тут и появляется Transactional Outbox Pattern.
Что именно делает этот паттерн?
Избавляет от необходимости писать в два места одновременно.
Превращает внешнюю запись в надежный процесс.
Идея простая, вот минимальный способ это реализовать:
1. Когда сохраняешь данные в базе (INSERT/UPDATE),
ты параллельно пишешь событие в отдельную таблицу, например outbox.
2. Оба INSERT выполняются в одной транзакции.
Если что-то падает, падает все.
Так достигается гарантированное консистентное состояние.
3. Затем отдельный процесс (poller или scheduler) читает эту таблицу и публикует реальное событие в Kafka, RabbitMQ или куда нужно.
4. Если публикация упала, ничего страшного.
Событие остается в таблице пока его не получится отправить.
С этим достаточно простым потоком ты получаешь консистентность без двойной записи.
Почему это так хорошо работает?
Потому что принимает неприятную правду:
Нельзя рассчитывать что два разных системы корректно обработают одну и ту же транзакцию.
Поэтому решение не в том чтобы это «продавить», а в том чтобы адаптировать архитектуру к реальности:
Единственная запись, которой реально можно доверять — запись в твою базу.
Все внешнее (то что не хранится в твоей базе) должно выполняться позже, с ретраями, логами и прочим.
Но да, это не бесплатно.
Нужно:
—> Создать таблицу outbox.
—> Настроить ретраи.
—> Удалять обработанные события.
—> Мониторить poller (или любую другую реализацию).
—> Не допустить двойные вставки в outbox чтобы избежать дублей.
И ключевая мысль:
Цена Transactional Outbox намного ниже цены ручной починки рассинхрона между сервисами и системами.
А это в проде дороже золота.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤2
Совет по Java: старайся не шарить данные между потоками.
Используй неизменяемые объекты
Или строй взаимодействие потоков через сообщения, без прямой модификации общего состояния
С неизменяемыми объектами:
Обмен сообщениями:
👉 Java Portal
Используй неизменяемые объекты
Или строй взаимодействие потоков через сообщения, без прямой модификации общего состояния
С неизменяемыми объектами:
record Book(String title, int price) {} // Immutable
public class BookJob implements Runnable {
private final Book book;
... // constructor
@Override
public void run() {
System.out.println(book.title() + " " + book.price());
}
}Обмен сообщениями:
...
new Thread(() -> {
try {
queue.put("mess1");
} catch (InterruptedException e) {}
}).start();
...
new Thread(() -> {
try {
String mess = queue.take();
} catch (InterruptedException e) {}
}).start();
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🤔1
GitHub теперь в Telegram!
Самый прогерский канал, где за 10 минут ты научишься:
/ Пробив по фото и номеру в ТГ
// Как взломать вебку подруги
/// Мануал по OSINT разведке
Подписывайся, нас уже сотни тысяч: >@GitHub
Самый прогерский канал, где за 10 минут ты научишься:
/ Пробив по фото и номеру в ТГ
// Как взломать вебку подруги
/// Мануал по OSINT разведке
Подписывайся, нас уже сотни тысяч: >@GitHub
🤣7🤔1
image_2025-12-04_09-41-20.png
1.4 MB
Знали, что у EnableWebSecurity есть параметр debug, который включает отладку безопасности?
🤯
Это помогает разобраться, что вообще происходит во время разработки.
Но в проде такое включать нельзя.
👉 Java Portal
Это помогает разобраться, что вообще происходит во время разработки.
Но в проде такое включать нельзя.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
Совет по Java: начиная с Java 19 можно потрогать фичу под названием virtual threads.
Virtual threads это лёгкие потоки, которые управляются JVM, а не операционной системой.
JVM умеет приостанавливать и возобновлять выполнение без лишней траты ресурсов ОС.
IO-вызовы не блокируют потоки ОС.
Виртуальные потоки работают поверх небольшого пула платформенных потоков.
Пример:
👉 Java Portal
Virtual threads это лёгкие потоки, которые управляются JVM, а не операционной системой.
JVM умеет приостанавливать и возобновлять выполнение без лишней траты ресурсов ОС.
IO-вызовы не блокируют потоки ОС.
Виртуальные потоки работают поверх небольшого пула платформенных потоков.
Пример:
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 10000; i++) {
executor.submit(() -> {
Thread.sleep(1000);
});
}
}Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤4
⚡️ ВАЙБ-КОДИНГ теперь в Telegram!
Ребята сделали крутейший канал, где на наглядных примерах и понятном языке рассказывают как войти в новую эру разработки с ИИ, делятся полезными фишками и инструментами
Подписывайтесь: @vibecoding_tg
Ребята сделали крутейший канал, где на наглядных примерах и понятном языке рассказывают как войти в новую эру разработки с ИИ, делятся полезными фишками и инструментами
Подписывайтесь: @vibecoding_tg
🤣6💊3❤1😁1
Нужна API, чтобы попрактиковаться в программировании?
Это, пожалуй, лучший вариант для приложений с прогнозом погоды.
✓ Полностью бесплатная
✓ Без регистрации и без API-ключа
✓ Работает по HTTPS и с включённым CORS
Можно тренироваться с JavaScript, Python, Java и чем угодно ещё:
→ open-meteo.com
👉 Java Portal
Это, пожалуй, лучший вариант для приложений с прогнозом погоды.
✓ Полностью бесплатная
✓ Без регистрации и без API-ключа
✓ Работает по HTTPS и с включённым CORS
Можно тренироваться с JavaScript, Python, Java и чем угодно ещё:
→ open-meteo.com
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3👍3🔥2
Java-совет: ты можешь использовать LinkedHashMap, чтобы легко реализовать LRU-кеш (Least Recently Used).
Тебе просто нужно заинстанцировать это вот так:
👉 Java Portal
Тебе просто нужно заинстанцировать это вот так:
int capacity = 3;
Map<Integer, String> cache = new LinkedHashMap<>(capacity, 0.75f, true) {
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > capacity;
}
};
cache.put(1, "one");
cache.put(2, "two");
cache.put(3, "three");
cache.get(1);
cache.put(4, "four"); // 2 удаляется (наименее недавно использованный)
System.out.println(cache);
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤4🤔1
freeCodeCamp выкатили бесплатный курс по Git и GitHub для новичков. За 1 час разберёшь базу: ветки, слияния, pull request’ы и базовую командную работу. Отличный быстрый вход для тех, кто откладывал Git «на потом».
Git-курс тут
👉 Java Portal
Git-курс тут
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3