Що таке 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
Внутрішня архітектура брокера: 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), а Брокери 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 впав нижче мінімуму. Повідомлення залишилося в лозі лідера, але продюсеру повертається помилка (при повторі з ідемпотентністю дублювання не відбудеться).
🎯 Шпаргалка для інтерв’ю
Головне про 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.