Outbox pattern

В проектировании есть классическая проблема dual write: нужно обновить несколько мест.

Возможные сценарии отказов

Сценарий 1: Успешная запись в БД + отказ Kafka

  1. Places Service успешно сохраняет данные о заведении
  2. Kafka недоступна или происходит network failure
  3. Событие теряется, downstream сервисы не получают уведомление
  4. Результат: система в inconsistent состоянии

Сценарий 2: Отказ БД + успешная отправка в Kafka

  1. Событие успешно отправлено в Kafka
  2. Запрос в PostgreSQL упал
  3. Результат: downstream сервисы получили событие о несуществующем изменении

Этап 1: обновляем данные в PG и в outbox через транзакцию. Outbox может быть как внутри одной БД, так и в рамках отдельной БД.

Ниже SQL пример. В коде это также делается через транзакцию

BEGIN TRANSACTION;
-- Обновляем основную таблицу
UPDATE places SET name = 'New Name' WHERE id = 123;
-- Записываем событие в outbox
INSERT INTO outbox (event_type, payload, aggregate_id) 
VALUES ('PlaceUpdated', '{"id": 123, "name": "New Name"}', 123);
COMMIT;

Этап 2: вычитываем из outbox и пишем в Kafka

Этап 3: чистим outbox

💡Как доставлять данные из outbox в Kafka?

  • Можно через отдельный worker
  • Можно через CDC

Важно помнить, что мы выбираем семантику at-least-once. То есть на стороне Geo-Service нам нужно будет проверять на дубли — идемпотентность.