Где лучше сохранять информацию о том, что уведомление уже отправлено — на стороне продюсера или консьюмера
Что проверяют
Понимаете ли вы семантику доставки в брокере и где на самом деле рождаются дубли. Правильный ответ почти всегда «на стороне того, кто совершает побочный эффект», и следом спросят, как сделать это без гонок между несколькими экземплярами консьюмера.
Как отвечать
Начните с того, что Kafka и RabbitMQ в типичной настройке дают доставку «хотя бы один раз». Консьюмер может упасть после отправки письма, но до коммита offset, и после ребаланса сообщение придёт снова. Продюсер про это не знает: он видит только, что событие записано в топик. Поэтому факт «уведомление отправлено» хранит тот, кто его отправляет, то есть консьюмер.
Каркас решения:
- ключ идемпотентности: у события есть стабильный
notificationId, его задаёт продюсер при создании события, а не консьюмер при чтении; - таблица статусов: запись с уникальным ограничением на этот ключ и статусами вроде
PENDING,SENT,FAILED. Захват через уникальный индекс илиINSERT ... ON CONFLICT DO NOTHINGв PostgreSQL, а не через «сначала SELECT, потом INSERT»; - внешний провайдер: если его API принимает ключ идемпотентности, передавайте тот же
notificationId, тогда повтор не станет вторым SMS; - окно между отправкой и записью: полностью его не закрыть. Выбирайте осознанно: для кода подтверждения лучше повтор, для рекламной рассылки лучше пропуск.
У продюсера своя задача: не потерять событие. Для этого есть transactional outbox, запись события в той же транзакции, что и бизнес-изменение. Идемпотентный продюсер Kafka (enable.idempotence, включён по умолчанию с версии 3.0) убирает только дубли от ретраев самого продюсера внутри партиции.
Где спотыкаются
Говорят «включим exactly-once, и дублей не будет». Транзакции Kafka покрывают цикл «прочитал, обработал, записал в Kafka», но не отправку письма во внешний сервис.
Хранят отметку в HashMap или локальном кеше. После рестарта или на втором экземпляре консьюмера её уже нет.
Делают проверку и запись двумя шагами без уникального ограничения. Два экземпляра одновременно видят «ещё не отправлено» и оба шлют.