В чем разница между гарантиями доставки сообщений в Kafka: at least once, at most once, exactly once
Что проверяют
Понимаете ли вы, что гарантия складывается из настроек продюсера и порядка фиксации смещения у консьюмера, а не включается одной галочкой.
Как отвечать
Разберите по действиям, а не по названиям:
- at most once (не более одного раза): консьюмер фиксирует смещение сразу после получения, до обработки. Упал на обработке, сообщение потеряно. Потери допустимы там, где данные быстро обесцениваются, например метрики;
- at least once (хотя бы один раз): смещение фиксируется после успешной обработки. Упал после обработки, но до фиксации, сообщение придёт снова. Потерь нет, дубли есть. Это режим по умолчанию и самый частый выбор;
- exactly once (ровно один раз): дубли и потери исключены, но только внутри Kafka. Достигается идемпотентным продюсером (
enable.idempotence=true) и транзакциями, где запись результата и фиксация смещения происходят в одной транзакции. В Kafka Streams это включается настройкойprocessing.guarantee=exactly_once_v2.
Ключевая мысль, которую стоит проговорить: «ровно один раз» действует для цепочки Kafka → обработка → Kafka. Как только обработчик пишет во внешнюю базу или дёргает чужой API, транзакция туда не распространяется, и гарантия снова становится «хотя бы один раз». Поэтому на практике почти везде живут с дублями и делают обработку идемпотентной: ключ операции, проверка на уже обработанное, вставка с условием.
Полезно назвать цену: транзакции дают заметные накладные расходы и усложняют эксплуатацию, а идемпотентный обработчик обычно дешевле и надёжнее.
Где спотыкаются
Считают, что достаточно включить настройку, и дубли исчезнут в любой системе.
Фиксируют смещение автоматически по таймеру и не понимают, почему после падения часть сообщений пропала.
Забывают про порядок: он гарантирован только внутри партиции, и параллельная обработка его нарушает.