Как добиться идемпотентности при обработке сообщений в Kafka
Что проверяют
Понимаете ли вы, что Kafka в типичной настройке даёт доставку «хотя бы один раз», и дубли у консьюмера неизбежны при сбоях и ребалансировках. Следом обычно спрашивают, решает ли это enable.idempotence и что на самом деле означает exactly-once в Kafka.
Как отвечать
Продюсер. С Kafka 3.0 enable.idempotence=true и acks=all включены по умолчанию. Продюсер получает идентификатор PID, сообщения в партиции нумеруются, и брокер отбрасывает повтор, если пачку переотправили после таймаута. Это защита только от ретраев внутри одной сессии продюсера. Если сервис упал и после рестарта отправил то же событие заново, для брокера это новое сообщение.
Консьюмер. Сообщение придёт повторно, если сервис обработал его, но упал до коммита оффсета. Поэтому обработчик делают идемпотентным:
- ключ идемпотентности: у события есть бизнес-идентификатор (
eventId, номер платежа), продюсер кладёт его в заголовок или тело; - таблица обработанных: в той же транзакции БД, где меняются данные, делается
INSERTв таблицу с уникальным ключом поeventId. Конфликт уникальности означает, что событие уже обработано; - естественная идемпотентность: операцию формулируют как установку состояния (
status = PAID, upsert), а не как приращение баланса; - оффсеты в БД: позиция чтения хранится рядом с результатом и восстанавливается через
seek()при назначении партиций.
Транзакции Kafka (transactional.id, у консьюмера isolation.level=read_committed) дают exactly-once только в цепочке «прочитал из Kafka, записал в Kafka», как в Kafka Streams с processing.guarantee=exactly_once_v2. Запись во внешнюю базу они не покрывают.
Где спотыкаются
Считают, что enable.idempotence делает идемпотентной всю обработку. Он работает между продюсером и брокером и о базе консьюмера ничего не знает.
Проверяют дубль через SELECT, а потом делают INSERT без уникального ограничения. Два экземпляра после ребалансировки проходят проверку одновременно, и дубль записывается. Защищает только ограничение в базе.
Используют оффсет как ключ идемпотентности. Если продюсер отправил одно событие дважды, оффсеты у копий разные, и дубль пройдёт.