Kafka consumer groups на собеседовании Data Engineer

Готовься к собесу аналитика как в Duolingo
10 минут в день — SQL, Python, A/B, метрики. 1700+ вопросов в Telegram
Открыть Карьерник в Telegram

Карьерник — Duolingo для аналитиков: 10 минут в день тренируй SQL, Python, A/B, статистику, метрики и ещё 3 темы собеса. 1500+ вопросов в Telegram-боте. Бесплатно.

Зачем спрашивают на собесе DE

Kafka — стандарт стриминга. Consumer groups — основа распределённого consume'а. На собесе DE: «как работает rebalance», «зачем consumer group», «exactly-once vs at-least-once». Senior — тонкости static membership, incremental cooperative rebalancing, idempotent producers.

Главная боль без понимания — DE настроил consumer без понимания offset-коммита, при перезапуске обработали 1М сообщений дважды. Ловят на проде через неделю.

Что такое consumer group

Consumer group — набор consumers с общим group.id. Каждая partition топика читается ровно одним consumer'ом группы.

Topic "events" partitions [0, 1, 2, 3]

Consumer Group "ETL"
├─ Consumer A   → partitions [0, 1]
└─ Consumer B   → partitions [2, 3]

Если consumer'ов больше, чем partitions — лишние простаивают. Параллелизм consumer'а ограничен числом partitions.

Свойства:

  • Несколько групп могут читать тот же топик независимо (pub-sub).
  • Внутри группы — конкурентное чтение (queue).
  • Offsets хранятся per-group — каждая группа имеет свой прогресс.
  • Group coordinator (брокер) управляет membership и assignment.

Offsets и commit

Offset — позиция consumer в partition. После обработки сообщения consumer коммитит offset, чтобы не читать его снова.

Виды commit:

1. Auto-commit (enable.auto.commit=true):

auto.commit.interval.ms=5000  # коммит каждые 5 сек

Просто. Опасно — может закоммитить до обработки. Если процесс упал — потеряли сообщения, или обработали повторно.

2. Manual commit:

consumer.poll(timeout_ms=1000)
for msg in messages:
    process(msg)
consumer.commit_sync()    # явный commit после обработки

Корректнее. Гарантия at-least-once (не теряем).

3. Per-partition manual commit: для тонкого контроля при error recovery.

4. Transactional commit: с idempotent producer + transactions — exactly-once в рамках Kafka.

Rebalance и его проблемы

Когда происходит:

  • Consumer присоединяется или уходит из группы.
  • Меняется число partitions.
  • session.timeout.ms истёк (consumer не слал heartbeat).

Что происходит:

  1. Group coordinator (брокер) триггерит rebalance.
  2. Все consumers останавливают чтение.
  3. Coordinator пересчитывает assignment.
  4. Consumers получают новый набор partitions, начинают читать.

Stop-the-world. Во время rebalance вся группа стоит — обработка сообщений приостанавливается.

Проблемы:

  • При большой группе rebalance занимает секунды-минуты.
  • Если rebalance частые (флапающий consumer) — пропускная способность падает.
  • Network shouldn't drop — иначе постоянные rebalance.

Решения в Kafka 2.4+:

  • Static membership (group.instance.id). Consumer держит persistent ID. Перезапуск — не вызывает rebalance, если consumer вернулся в session.timeout.ms.
  • Incremental cooperative rebalancing (Kafka 2.4+). Не stop-the-world — partitions перераспределяются инкрементально.

Partition assignment стратегии

partition.assignment.strategy:

  • RangeAssignor (default до 2.4) — сортирует partitions, делит на consumers по диапазонам. Может давать перекос на разных топиках в одной группе.
  • RoundRobinAssignor — round-robin распределение. Равномерно.
  • StickyAssignor — сохраняет назначения через rebalance — минимум переключений.
  • CooperativeStickyAssignor (Kafka 2.4+) — sticky + cooperative. Дефолт в новых версиях.

В 2026 — CooperativeStickyAssignor рекомендуется для новых проектов.

Готовься к собесу аналитика как в Duolingo
10 минут в день — SQL, Python, A/B, метрики. 1700+ вопросов в Telegram
Открыть Карьерник в Telegram

Семантика доставки

At-most-once. Commit до обработки.

msg = poll()
commit()
process(msg)   # если упало — сообщение потеряно

Используется редко — данные дороже, чем дубликаты.

At-least-once. Commit после обработки.

msg = poll()
process(msg)
commit()       # если упало после process до commit — повтор обработки

Дефолт в большинстве пайплайнов. Получатель должен быть идемпотентен.

Exactly-once (EOS).

Несколько вариантов:

  1. Within Kafka (read from Kafka → write to Kafka) — transactional producer + read isolated. С 0.11+. Гарантия в рамках Kafka.

  2. Kafka → external sink — exactly-once требует двухфазного commit или idempotent receiver. Например, sink в Postgres с INSERT ... ON CONFLICT — at-least-once + idempotency = effectively exactly-once.

  3. Spark Structured Streaming + Kafka — Spark может быть exactly-once при правильном sink (Delta Lake, idempotent JDBC).

Idempotent producer. enable.idempotence=true — гарантирует, что producer retry не создаст дубликат на брокере.

Lag и мониторинг

Consumer lag = latest_offset - committed_offset. Сколько сообщений отстал consumer от хвоста топика.

Метрики для мониторинга:

  • Lag по partition / по group.
  • Throughput (messages/sec).
  • Rebalance count.
  • Heartbeat success rate.

Tooling:

  • kafka-consumer-groups.sh --describe — встроенная.
  • Burrow / Kafka Lag Exporter / Cruise Control — production-grade.
  • Confluent Control Center.

Что делать при росте lag:

  • Добавить consumer'ов (если partitions > consumers).
  • Увеличить max.poll.records.
  • Оптимизировать processing (бутылочное горлышко часто в downstream).
  • Параллелизовать processing внутри consumer'а (но не max.poll.interval.ms issue).
  • Reshard topic (увеличить число partitions).

Частые ошибки

Auto-commit + долгое processing. Auto-commit может закоммитить, пока process ещё идёт — при падении не восстановим.

max.poll.interval.ms слишком маленький. Если processing превышает interval — consumer выкидывается из группы → rebalance → дубликат обработки.

Один partition на топик. Параллелизма consumer нет — узкое место. На production — минимум 3-6 partitions, но можно сотни.

Не фиксировать group.id. Без group.id consumer работает в anonymous mode (с временным id) — нет offset commit. На рестарт — читает с начала или с конца.

Изменять число partitions топика. Существующие сообщения пере-распределяются по partition'ам через hash key — порядок ломается. Партиционирование — design-time decision.

Не использовать idempotent producer. Без него retry → дубликаты на брокере.

enable.auto.commit=true для критичных пайплайнов. Лучше manual commit с явным контролем.

Игнорировать lag. Lag растёт = consumer не справляется. Если 1 час lag в реалтайм-пайплайне — продукт ломается.

Использовать одинаковый group.id для разных пайплайнов. Они начнут конкурировать за partitions. Каждый pipeline = свой group.

Связанные темы

FAQ

Сколько partitions оптимально?

Зависит от throughput. Эмпирика: один partition держит 10-50 МБ/сек. Для 500 MB/sec — 10-50 partitions. Total partitions per cluster — обычно до десятков тысяч.

Можно ли разделить partition между двумя consumers одной группы?

Нет. Один partition — один consumer в группе. Это by-design (sequential ordering within partition).

Что если consumer падает посреди commit?

Транзакционный manual commit — атомарен (как и любой broker write). Если commit ack не дошёл до consumer — повторный poll вернёт те же сообщения, обработка дублируется. Идемпотентность downstream спасает.

acks=all для consumer есть?

acks — параметр producer, не consumer. У consumer — параметры fetch (fetch.min.bytes, fetch.max.wait.ms).

Как читать с начала топика?

auto.offset.reset=earliest (если нет committed offset) или consumer.seek_to_beginning() явно.

Это официальная информация?

Нет. Статья основана на документации Apache Kafka 3.x и Confluent.


Тренируйте Data Engineering — откройте тренажёр с 1500+ вопросами для собесов.