Что такое стратегии доставки в Kafka и зачем они нужны
Что проверяют
Понимаете ли вы, что гарантия доставки это свойство всей цепочки, а не галочка в конфиге. Проверяют на стыках: что делает продюсер при повторной отправке, когда брокер считает запись состоявшейся, в какой момент потребитель коммитит оффсет.
Как отвечать
Уровня три: не более одного раза, не менее одного раза и ровно один раз. Первый допускает потерю, второй дубликаты, третий обещает отсутствие и того и другого.
На стороне продюсера за это отвечают acks и идемпотентность. При acks=0 подтверждения нет вовсе, при acks=1 достаточно лидера, и падение лидера до репликации теряет сообщение. Боевой вариант это acks=all вместе с min.insync.replicas больше единицы. Идемпотентный продюсер нумерует записи и не даёт повторной отправке породить дубль в партиции, он же удерживает порядок при нескольких запросах в полёте.
На стороне потребителя всё решает момент коммита оффсета. Коммит до обработки даёт «не более одного раза», коммит после успешной обработки даёт «не менее одного раза». В Go это означает отказ от автокоммита: клиентская библиотека двигает оффсет по таймеру, а не по факту вашей работы.
Ровно один раз в Kafka существует, но только внутри Kafka: транзакции продюсера плюс isolation.level=read_committed закрывают схему «прочитал, обработал, записал в топик». Как только обработка пишет в базу или дёргает чужой сервис, гарантия заканчивается, и практический ответ звучит так: не менее одного раза плюс идемпотентный потребитель, который дедуплицирует по ключу события.
Где спотыкаются
Обещают exactly once как общий режим и не оговаривают границу. Запись во внешнюю систему транзакцией Kafka не покрывается, и без дедупликации на своей стороне повтор после сбоя проведёт операцию дважды.
Оставляют автокоммит и обрабатывают сообщение асинхронно. Оффсет уезжает вперёд, падение процесса съедает всё, что было в работе, а выглядит потеря случайной, потому что воспроизводится только под нагрузкой.
Считают acks=all достаточным при min.insync.replicas равном единице. Тогда все синхронные реплики это одна живая, и подтверждённая запись исчезает вместе с ней.