Какие гарантии доставки сообщений предоставляет Kafka
Kafka предоставляет три уровня гарантий доставки:
Уровень Junior
Три уровня гарантий
Kafka предоставляет три уровня гарантий доставки:
1. At most once (не более одного раза):
Сообщение доставится максимум один раз
Может потеряться при ошибках
Как теряется: продюсер отправил и забыл (acks=0). Если сеть упала — сообщение потеряно. Консьюмер автокоммитит offset до обработки — если консьюмер упал после коммита но до обработки — сообщение потеряно.
2. At least once (как минимум один раз):
Сообщение доставится минимум один раз
Может продублироваться при retry
3. Exactly once (ровно один раз):
Сообщение доставится ровно один раз
Работает ТОЛЬКО для Kafka-to-Kafka сценариев. Для финансовых операций с записью в БД нужен Outbox pattern или idempotent writes.
Визуализация
At most once:
Продюсер → Kafka → (возможно потеряно) → Консьюмер
At least once:
Продюсер → Kafka → Консьюмер → (возможно дубликат)
Exactly once:
Продюсер → Kafka → Консьюмер → ровно один раз
Простая настройка
// At most once
props.put("acks", "0");
// At least once (рекомендуется)
props.put("acks", "all");
props.put("enable.idempotence", "true");
// Exactly once
props.put("enable.idempotence", "true");
props.put("isolation.level", "read_committed");
Когда НЕ использовать каждую семантику
- At-most-once — НЕ для платежей, заказов, уведомлений
- At-least-once — НЕ когда дубликаты критичны (без идемпотентной обработки)
- Exactly-once — НЕ для Kafka-to-Database, НЕ для Kafka-to-HTTP
Уровень Middle
Настройка гарантий
At most once:
props.put("acks", "0");
props.put("enable.idempotence", "false");
props.put("enable.auto.commit", "true");
// Быстро, но возможна потеря данных
At least once:
props.put("acks", "all");
props.put("enable.idempotence", "true");
props.put("retries", Integer.MAX_VALUE);
props.put("enable.auto.commit", "false");
// Надёжно, но возможны дубликаты
Exactly once:
// Producer
props.put("enable.idempotence", "true");
props.put("acks", "all");
// Consumer
props.put("isolation.level", "read_committed");
props.put("enable.auto.commit", "false");
Transaction API для Exactly Once
producer.initTransactions();
try {
producer.beginTransaction();
// Чтение
ConsumerRecords<String, String> records = consumer.poll();
// Обработка
for (var record : records) {
process(record);
}
// Запись результата
producer.send(new ProducerRecord<>("output", result));
// Коммит обоих действий
producer.commitTransaction();
} catch (Exception e) {
// Откат при ошибке
producer.abortTransaction();
}
Типичные ошибки
- At least once без идемпотентности:
Retry → дубликаты в топике → обработка дважды - Коммит до обработки:
consumer.commitSync(); // сначала коммит process(records); // потом обработка // Если упал → данные потеряны - Exactly once без transaction:
Producer и consumer не в одной транзакции → Возможна потеря или дублирование
Уровень Senior
Internal Implementation
Idempotent Producer:
Каждый producer получает уникальный PID (Producer ID)
Каждое сообщение получает Sequence Number
Брокер отслеживает последовательность
Дубликат с тем же PID + Sequence → отклоняется
Transaction Coordinator:
Отдельный компонент координирует транзакции
Хранит state транзакций в __transaction_state topic
Обеспечивает atomic commit/abort
Exactly Once — ограничения
Exactly Once работает только для:
Kafka → Kafka (read-process-write)
Не работает для:
Kafka → Database (Kafka не управляет транзакцией БД)
Kafka → HTTP API (нет guarantees на стороне API)
Решение для внешних систем:
1. Idempotent writes в БД (unique constraints)
2. Two-phase commit (XA — не рекомендуется)
3. Outbox pattern
4. Debezium CDC
End-to-End Exactly Once
Сценарий: Kafka → Processing → PostgreSQL
Решение: Outbox Pattern
1. Записать в outbox таблицу (в той же транзакции)
2. Debezium CDC читает outbox
3. Отправляет в Kafka
4. Consumer обрабатывает и пишет в target table
5. Idempotent upsert по business key
Failure Scenarios
1. Producer failure:
Отправил → не получил ack → retry
Idempotent producer → дубликат отклонён
2. Broker failure:
Записал в Leader → не успел реплицировать → Leader упал
acks=all + min.insync.replicas=2 → данные сохранены
3. Consumer failure:
Прочитал → не обработал → не закоммитил
При рестарте → прочитает снова (at least once)
Idempotent processing → дубликат обработан корректно
Performance Trade-offs
| Гарантия | Latency | Throughput | Data Safety |
|---|---|---|---|
| At most once | Минимальная | Максимальная | Низкая |
| At least once | Средняя | Высокая | Высокая |
| Exactly once | Высокая | Средняя | Максимальная |
Best Practices
✅ At least once по умолчанию
✅ Exactly once когда критично (финансы, биллинг)
✅ Идемпотентная обработка на консьюмере
✅ Idempotent producer (enable.idempotence=true)
✅ Transactional read-process-write для Kafka-to-Kafka
✅ Outbox pattern для внешних систем
❌ At most once для важных данных
❌ Без обработки дубликатов
❌ Exactly once без понимания ограничений
❌ Коммит до обработки
Архитектурные решения
- At least once + idempotent consumer — оптимальный баланс
- Exactly only for Kafka-to-Kafka — не работает с внешними системами
- Outbox pattern — для end-to-end guarantees
- Idempotent writes — универсальное решение для внешних систем
Резюме для Senior
- Exactly Once работает только в сценарии Kafka-to-Kafka
- Для внешних систем используйте Outbox pattern или idempotent writes
- At least once + idempotent consumer — оптимальный подход
- Transaction API обеспечивает atomic read-process-write
- Понимание ограничений критично для правильной архитектуры
🎯 Шпаргалка для интервью
Обязательно знать:
- Три гарантии: at-most-once (0-1 раз), at-least-once (1+ раз), exactly-once (ровно 1)
- At-most-once:
acks=0, быстро но возможна потеря данных - At-least-once:
acks=all+enable.idempotence=true, надёжно но возможны дубликаты - Exactly-once: Transaction API, работает ТОЛЬКО для Kafka-to-Kafka сценариев
- Для внешних систем (БД, HTTP) — Outbox pattern или idempotent writes
- Idempotent producer: PID + Sequence Number, брокер отклоняет дубликаты
- Коммит ДО обработки = потеря данных; коммит ПОСЛЕ = at-least-once
Частые уточняющие вопросы:
- Почему exactly-once не работает для Kafka → БД? — Kafka не управляет транзакцией БД.
- Как обеспечить end-to-end гарантию? — Outbox pattern: запись в outbox_table + CDC (Debezium).
- Что делает idempotent producer? — Уникальный PID + sequence number, брокер отклоняет дубликаты при retry.
- Какую гарантию выбрать по умолчанию? — At-least-once + идемпотентная обработка консьюмера.
Красные флаги (НЕ говорить):
- «Exactly-once работает для любых систем» — только Kafka-to-Kafka
- «At-most-once подходит для платежей» — потеря данных недопустима
- «Коммит перед обработкой — нормально» — при crash данные потеряны
- «Idempotent producer не нужен» — без него retry = дубликаты
Связанные темы:
- [[10. В чём разница между at-most-once, at-least-once и exactly-once]]
- [[11. Как настроить exactly-once семантику]]
- [[23. Что такое idempotent producer]]
- [[20. Что такое producer acknowledgment и какие режимы существуют (acks=0,1,all)]]