Как консьюмер Kafka определяет, с какого сообщения продолжить чтение?
Что проверяют
Знаете ли вы, где живёт позиция консьюмера и что происходит при рестарте, ребалансе и потере сохранённого оффсета. Для QA это прямой вход в вопрос, который обычно звучит следом: как вы проверите, что сервис после падения не теряет и не дублирует сообщения.
Как отвечать
Позиция считается отдельно для каждой тройки «группа, топик, партиция»:
- committed offset: консьюмер коммитит оффсет в служебный топик
__consumer_offsets. Хранится номер следующего сообщения к чтению, то есть последний обработанный плюс один; - старт и ребаланс: получив партицию, консьюмер спрашивает координатора группы о сохранённом оффсете и читает с него. Поэтому смена
group.idдаёт новую группу без истории; auto.offset.reset: срабатывает, только когда сохранённого оффсета нет или он указывает на данные, уже удалённые по ретеншену. По умолчаниюlatest, вариантыearliestиnone(исключение);- режим коммита: по умолчанию
enable.auto.commit=true, коммит раз вauto.commit.interval.ms(5 секунд) внутриpoll(). Ручной режим даётcommitSync()иcommitAsync()после обработки. Переставить позицию можно черезseek()илиkafka-consumer-groups.sh --reset-offsets, группа при этом должна быть остановлена.
Для тестирования отсюда вытекают конкретные проверки: убить консьюмер посреди пачки и посмотреть на дубли, проверить идемпотентность обработчика, следить за лагом через kafka-consumer-groups.sh --describe --group <имя> (колонки CURRENT-OFFSET, LOG-END-OFFSET, LAG).
Где спотыкаются
Говорят, что оффсеты хранятся в ZooKeeper. Так было у старого консьюмера много лет назад, а в Kafka 4.0 ZooKeeper удалён полностью.
Думают, что auto.offset.reset=earliest заставит существующую группу перечитать топик с начала. Если оффсет уже закоммичен, настройка ни на что не влияет.
Считают, что автокоммит гарантирует доставку ровно один раз. При падении после обработки, но до коммита, сообщения придут повторно, а при асинхронной обработке в другом потоке их можно и потерять.