Postgres и Kafka, часть 1



Возвращаюсь к ведению канала, и на этой неделе вас ждут 2 огненных поста🔥🔥 о взаимодействии Postgres и Kafka.



Эта пара безумно популярна на большинстве проектов👯‍♀️ База хранит данные, кафка отвечает за коммуникации между сервисами. Сегодня разберу одну проблему в их отношениях и расскажу вариант решения. А в следующем посте обсудим ещё 2 способа.



Рассмотрим проблему на простом примере.



Пользователь создал заказ, сервис принял запрос. Сервис добавил заказ в базу, и хочет рассказать другим сервисам о новом заказе. Что-то вроде такого:

public Order saveOrder(…) {

Order saved = orderRepo.save(…);

kafkaTemplate.send("orders",new OrderCreated(…));

}



Другие сервисы, подписанные на orders, получат сообщение и что-то сделают. Посчитают скидки, обновят статистику, запишут заказ в свою БД и тд.



В чём проблема?



Мы обращаемся к двум отдельным компонентам — базе данных и брокеру сообщений. Каждый из них в любой момент может отвалиться, например, пропадёт связь по сети. В зависимости от порядка строк в saveOrder возможны 2 негативных исхода:



😢 запись в базу сделали, сообщение не отправили

😢 отправили сообщение, но запись в БД не прошла



Получим несоответствие. Поэтому иногда хочется, чтобы события выполнились атомарно: либо оба успешно завершаются, либо ни одно из них.



Большинство разработчиков нетерпеливо скажут: "Что тут думать, нужен transaction outbox!!1". Но если спросить 10 человек, что они под этим понимают, получится 10 разных ответов.



В лучших традициях канала обсудим всё простыми словами:) Очень грубо все решения можно назвать так:

1️⃣ Убираем кафку

2️⃣ Убираем БД

3️⃣ Добавляем координатор



Сегодня рассмотрим первый вариант, в следующем посте — остальные два.



Вариант 1: убираем кафку



У Postgres есть механизм notify/listen, который отправляет уведомления заинтересованным лицам. И вместо отправки сообщений через кафку мы возьмём механизм подписки внутри БД.



База становится единственным компонентом и выполняет оба действия (сохранить в таблицу, уведомить заинтересованных) в одной транзакции.



Чтобы не решать проблемы с координацией двух компонентов, мы переложили всю работу на один.



Образцовая транзакция: атомарность и доставка exactly once

Минимальная задержка между сохранением в базу и уведомлением

Ограниченная функциональность уведомлений

Размытие ответственности — часть уведомлений делает Kafka, часть — Postgres

Увеличение нагрузки на БД



Последний пункт — главный ограничитель, поэтому подход "база делает всё" не очень популярен.



Реализация



Можно взять spring-integration-jdbc и для отправки сообщений, и для получения уведомлений. Документация максимально скудная, дополнительные детали есть в этой статье (под VPN)



В следующем посте обсудим ещё 2 варианта🔥