EventRelay
Платёж и событие сохраняются одной транзакцией и уходят в Kafka через outbox. Получатель помнит обработанные события, поэтому повторная доставка ничего не ломает.
Почта с одним конвертом
Платёж и конверт события ложатся в ящик одним движением. Каретка отправляет конверт по трубе, а у получателя наколка с номерами: повторный конверт падает в лоток «уже было».
Проблема
Процесс может упасть после того, как Kafka приняла событие, но до того, как база отметила его отправленным. Тогда событие уйдёт ещё раз. Значит, доставка будет «хотя бы один раз», и повтор должен быть безвредным.
Решение
Платёж и строка outbox записываются в PostgreSQL одной транзакцией. Несколько отправителей разбирают строки через FOR UPDATE SKIP LOCKED и шлют в Kafka с acks=all; при ошибке — повтор с паузой от секунды до пяти минут, после восьми попыток строка уходит на разбор оператору. Получатель записывает номер события вместе с обновлением проекции, поэтому повторная доставка ничего не меняет.
Технически
- Transactional outbox: платёж и событие в одной транзакции.
- Параллельные отправители без двойного захвата строк.
- Экспоненциальные повторы и статус DEAD после восьми попыток.
- Идемпотентный получатель: обработанные события хранятся с проекцией.
- Битые сообщения после трёх попыток уходят в отдельную тему.