Что такое producer acknowledgment и какие режимы существуют (acks=0,1,all)
Начиная с Kafka 3.0, значение по умолчанию изменено с acks=1 на acks=all совместно с включенной по умолчанию идемпотентностью (enable.idempotence=true).
🟢 Junior Level
30-секундный ответ
Producer Acknowledgment (acks) — это параметр конфигурации продюсера в Apache Kafka, определяющий, скольким брокерам (репликам) необходимо подтвердить получение сообщения перед тем, как продюсер сочтет отправку успешной:
acks=0(Fire and Forget): продюсер не ожидает ответа от брокера. Сообщение считается отправленным сразу после помещения в локальный буфер сокета. Максимальная пропускная способность, нулевая гарантия доставки.acks=1(Leader Only): продюсер ждет подтверждения только от брокера-лидера партиции. Если лидер упадет до того, как реплики скопируют запись, данные будут потеряны.acks=all(илиacks=-1) (Leader + ISR): продюсер ждет подтверждения от лидера и всех синхронизированных реплик (In-Sync Replicas, ISR). Максимальная надежность (приmin.insync.replicas >= 2).
Начиная с Kafka 3.0, значение по умолчанию изменено с acks=1 на acks=all совместно с включенной по умолчанию идемпотентностью (enable.idempotence=true).
Наглядная аналогия из жизни
acks=0(Обычное письмо в синий уличный ящик): бросил конверт в щель ящика и пошел дальше. Никаких квитанций; сгорел ящик — письмо пропало.acks=1(Передача курьеру в руки): курьер расписался в накладной и забрал посылку. Но если курьер попадет в аварию по дороге в сортировочный центр — посылка не дойдет.acks=all(Банковская ячейка с тремя ключами): главный клерк и его доверенные ассистенты (ISR) одновременно регистрируют слиток в своих журналах. Только когда запись сделана у каждого клерка, клиенту выдается сертификат.
Пример настройки в Java
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
// Конфигурация максимальной надежности
props.put(ProducerConfig.ACKS_CONFIG, "all");
props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, "true");
props.put(ProducerConfig.RETRIES_CONFIG, Integer.MAX_VALUE);
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
🟡 Middle Level
Детальная механика работы режимов
acks=0:
Producer ---- [Message] ----> (Socket Buffer) ----> Сразу Success! (не ждет брокера)
acks=1:
Producer ---- [Message] ----> Broker Leader (запись в Page Cache) ----> Success!
|
(асинхронно реплицируется фолловерами)
acks=all:
Producer ---- [Message] ----> Broker Leader (Log.appendAsLeader)
|-- репликация на Follower 1 (ACK)
|-- репликация на Follower 2 (ACK)
v
Только когда все ISR подтвердили ----> Success!
Почему acks=all не спасает без min.insync.replicas
Параметр acks=all требует подтверждения от всех участников текущего ISR.
- Если у топика
replication.factor=3, но два брокера упали, в ISR останется ровно один узел (сам лидер). - В этом состоянии
acks=allпотребует подтверждения только от этого единственного лидера, деградируя доacks=1! - Если оставшийся лидер аварийно завершится до поднятия упавших нод — подтвержденные сообщения будут утеряны.
Золотое правило надежности Kafka:
replication.factor = 3min.insync.replicas = 2acks = all
Если размер ISR становится меньше min.insync.replicas, лидер отклоняет запись продюсера и возвращает NotEnoughReplicasException.
Сравнительная таблица режимов
| Режим | Подтверждение от кого | Latency (задержка) | Throughput | Риск потери данных | Поддержка Retries | Типичный Use Case |
|---|---|---|---|---|---|---|
acks=0 |
Никого (буфер сокета) | < 1 ms | Максимальный | Очень высокий | ❌ Невозможен | Метрики, кликстрим, GPS-трекинг датчиков |
acks=1 |
Только Лидер | 2–5 ms | Высокий | Средний (смерть лидера до sync) | ✅ Да | Логирование, кэш-инвалидация |
acks=all |
Лидер + весь ISR | 5–20 ms | Высокий (при батчинге) | Нулевой (при min.insync >= 2) |
✅ Да | Платежи, заказы, биллинг, критичные события |
🔴 Senior Level
Внутренняя архитектура брокeра: DelayedProduce и Purgatory
Когда брокер-лидер получает ProduceRequest с acks=all, он не блокирует сетевой поток KafkaRequestHandler:
- Запись в локальный лог: Лидер выполняет
Log.appendAsLeader(), сохраняя батч сообщений в Page Cache своей ОС. - Помещение в Purgatory: Создается объект
DelayedProduce, помещаемый в структуру данных DelayedOperationPurgatory (иерархический таймер на основе тайм-колес Hierarchical Timing Wheels). - Репликация фолловерами: Фолловеры запрашивают данные через
FetchRequest. По мере подтверждения смещений (LEO) лидер обновляет отслеживаемый статус репликации. - Сдвиг High Watermark (HW): Когда все члены текущего ISR догнали оффсет нового батча, High Watermark сдвигается вперед.
- Завершение DelayedProduce: Purgatory триггерит callback, формируется
ProduceResponse, и продюсер получает подтверждение об успешной записи.
Producer Broker (Leader) Follower (ISR)
| | |
|--- ProduceRequest (acks=all) ->| |
| |-- 1. Log.appendAsLeader() |
| |-- 2. Register DelayedProduce |
| | in Purgatory |
| | |
| |<--- FetchRequest (LEO update) --|
| |--- FetchResponse -------------->|
| | |
| |-- 3. HW advanced to match LEO |
| |-- 4. Complete DelayedProduce |
|<-- ProduceResponse (Success) --| |
Взаимодействие ACKS с Idempotence и max.in.flight.requests
Для предотвращения дублирования и нарушения порядка сообщений при ретраях:
enable.idempotence=trueтребует:acks=all(или-1)retries > 0max.in.flight.requests.per.connection <= 5
- Каждому сообщению присваивается монотонный
SequenceNumberв рамках продюсера (ProducerId). Если лидер получает повтор батча (например, из-за потери ACK по таймауту), он подтверждает получение продюсеру, но не записывает дубликат в лог.
4 Tricky Questions
1. Почему связка acks=all и replication.factor=3 без настройки min.insync.replicas может привести к потере подтвержденных сообщений?
Ответ:
По умолчанию в ранних версиях Kafka или при небрежной настройке топика min.insync.replicas=1.
- Представим топик с 3 репликами. Брокеры 2 и 3 выходят из строя.
- Контроллер исключает брокеры 2 и 3 из ISR. В ISR остается только один Брокер 1 (Лидер).
- Продюсер с
acks=allшлет сообщение. Лидер проверяет: «Все ли члены текущего ISR подтвердили запись?». Поскольку в ISR только он сам, запись фиксируется немедленно, и продюсеру возвращается Success! - Если через секунду Брокер 1 сгорает (kernel panic/hard reset), а Брокеры 2 и 3 возвращаются в строй, новым лидером станет узел без этих данных. Подтвержденные сообщения безвозвратно потеряны.
- Защита: установка
min.insync.replicas=2заставляет Лидера немедленно выбрасыватьNotEnoughReplicasException, блокируя запись в уязвимом состоянии.
2. Может ли продюсер с acks=0 исчерпать память и упасть с OutOfMemoryError или заблокироваться?
Ответ: Да, может.
- При
acks=0продюсер не ждет ответа от брокера, но сообщения все равно помещаются во внутренний буферRecordAccumulatorперед отправкой по сети фоновым потокомSender. - Если скорость генерации сообщений приложением превышает пропускную способность сетевого сокета или буфер брокера переполнен, буфер
buffer.memory(по умолчанию 32 МБ) заполнится целиком. - В этот момент метод
producer.send()заблокируется на времяmax.block.ms(по умолчанию 60 секунд), после чего выброситTimeoutException. - Если приложение не обрабатывает ошибки и накапливает ссылки на объекты в heap, это приведет к
OutOfMemoryError.
3. Брокер находится в тяжелой Stop-the-World паузе GC. Как это повлияет на продюсеров с acks=all?
Ответ:
- Если завис фолловер, входящий в ISR:
- Лидер ожидает ответа от этого фолловера, удерживая запрос продюсера в Purgatory.
- Продюсеры испытывают резкий рост latency вплоть до значения
request.timeout.ms(по умолчанию 30с). - Лидер ждет таймаута
replica.lag.time.max.ms(по умолчанию 30с). По истечении этого времени лидер объявляет фолловер выбывшим и исключает его из ISR (ISR Shrink). - Как только размер ISR уменьшился, условие записи считается выполненным, и продюсер получает запоздалый Success.
- Если завис сам лидер: продюсер ждет
request.timeout.ms, получаетTimeoutExceptionи повторяет отправку (retries).
4. В чем разница между NotEnoughReplicasException и NotEnoughReplicasAfterAppendException?
Ответ:
NotEnoughReplicasException: возвращается до попытки записи в локальный журнал лидера. Лидер проверил текущий размер ISR, увиделsize(ISR) < min.insync.replicasи немедленно отклонил запрос продюсера. Лог лидера не изменен.NotEnoughReplicasAfterAppendException: брокер успешно записал сообщение в свой локальный лог (так как в момент старта размер ISR был достаточен), но в процессе ожидания подтверждения от фолловеров реплики отвалились, и размер ISR упал ниже минимума. Сообщение осталось в логе лидера, но продюсеру возвращается ошибка (при retry с идемпотентностью дублирования не произойдет).
🎯 Шпаргалка для интервью
Главное о Producer Acknowledgment
acks=0: огонь и забыл; нулевая задержка, нулевая гарантия, нет ретраев.acks=1: подтверждение от лидера; безопасно при стабильной работе, риск потери при аварии лидера до репликации.acks=all: подтверждение от лидера и всех участников ISR. Стандарт по умолчанию в Kafka 3.0+.- Формула надежности:
replication.factor=3+min.insync.replicas=2+acks=all+enable.idempotence=true. - Purgatory: механизм асинхронного удержания запросов
acks=allна брокере без блокировки потоков ввода-вывода.
Матрица режимов ACKS
| Параметр | acks=0 | acks=1 | acks=all | | :— | :— | :— | :— | | Кем подтверждается | Локальный сокет | Только Лидер | Лидер + все члены ISR | | Гарантия доставки | At-most-once | At-least-once (с риском) | At-least-once / Exactly-once (с идемпотентностью) | | Потери при падении лидера | Вероятны | Возможны | Исключены (при ISR >= 2) | | Дефолтное значение | Kafka 0.8.x | Kafka 0.9.x – 2.8.x | Kafka 3.0+ |
Красные флаги на собеседовании (Чего говорить нельзя)
- ❌ «При acks=all продюсер сам отправляет сообщения параллельно на все брокеры топика» — продюсер общается исключительно с лидером партиции.
- ❌ «acks=all гарантирует стопроцентное отсутствие дубликатов» —
acks=allотвечает за надежность сохранения; от дубликатов защищает идемпотентность (enable.idempotence=true). - ❌ «Если acks=all, сообщение сразу становится доступным консьюмерам» — консьюмеры читают строго до High Watermark, поэтому сообщение становится доступным только после фиксации всем ISR.