Вопрос 26 · Раздел 15

Как мониторить lag консьюмера в Kafka

Представьте очередь в магазине. Кассир (consumer) обслужил 80 покупателей, а в очереди стоит ещё 200. Lag = 200 — это сколько покупателей ещё ждут. Если lag растёт — кассир не с...

Версии по языкам: English Russian Ukrainian

🟢 Junior Level

Простое определение

Consumer lag — это разница между последним сообщением в партиции Kafka и последним сообщением, которое обработал консьюмер. Показывает насколько консьюмер “отстаёт”.

Аналогия

Представьте очередь в магазине. Кассир (consumer) обслужил 80 покупателей, а в очереди стоит ещё 200. Lag = 200 — это сколько покупателей ещё ждут. Если lag растёт — кассир не справляется, нужно открыть ещё кассу.

Визуализация

Partition 0:
  Log End Offset (последнее в Kafka):  1000
  Committed Offset (обработано):        800
  ────────────────────────────────────────
  Consumer Lag:                         200

  [0......100......200......800|←200→|1000]
  |----- обработано -----| lag |

Как посмотреть lag

# Kafka CLI утилита
kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --describe --group order-service

GROUP           TOPIC    PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG
order-service   orders   0          800             1000            200
order-service   orders   1          750             900             150
order-service   orders   2          900             950              50

Total lag: 400 сообщений

Что значит lag

Lag Что означает Что делать
0 Консьюмер обработал всё Ничего, всё отлично
1–100 Нормальное отставание Наблюдать
100–1000 Консьюмер замедлился Проверить throughput
1000+ Консьюмер не справляется Масштабировать
Растёт быстро Критическая проблема Alert + investigate

(эти значения зависят от throughput — lag нужно оценивать ВМЕСТЕ с producer rate. Lag 1000 при 100 msg/s = 10 секунд отставания; при 1M msg/s = несущественно)

Когда это важно

  • Production monitoring — всегда мониторить lag
  • Scaling decisions — когда добавлять консьюмеры
  • Health checks — lag = индикатор здоровья
  • SLA monitoring — guarantee processing time

🟡 Middle Level

Как работает lag calculation

Lag = LogEndOffset - CommittedOffset

LogEndOffset:
  - Последний offset записанный в партицию
  - Обновляется при каждом producer send
  - Хранится на broker

CommittedOffset:
  - Последний offset закоммиченный консьюмером
  - Хранится в __consumer_offsets topic
  - Обновляется при consumer.commitSync/Async

Lag calculation:
  - Вычисляется external tool (не самой Kafka)
  - Kafka broker предоставляет LogEndOffset
  - Monitoring tool читает committed offsets
  - Lag = разница между ними

Инструменты мониторинга

1. Kafka CLI

# Все consumer groups
kafka-consumer-groups.sh --bootstrap-server localhost:9092 --list

# Детали конкретной группы
kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --describe --group order-service

# Reset offsets (осторожно!)
kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
  --group order-service --reset-offsets --to-latest --execute

2. Burrow (LinkedIn)

# Burrow HTTP API
GET http://burrow-server:8000/v3/kafka/cluster/consumer/order-service/status

Response:
{
  "error": false,
  "message": "consumer status returned",
  "status": {
    "cluster": "production",
    "group": "order-service",
    "status": "OK",
    "complete": 1.0,
    "partitions": [
      {
        "topic": "orders",
        "partition": 0,
        "status": "OK",
        "start": {"offset": 700, "timestamp": 1712000000000},
        "end": {"offset": 1000, "timestamp": 1712000100000},
        "current_lag": 200,
        "max_lag": 300
      }
    ],
    "partition_count": 3
  }
}

Burrow status values: | Status | Значение | | ——– | ———————– | | OK | Lag уменьшается или 0 | | WARN | Lag стабильный, высокий | | ERROR | Lag растёт | | STOP | Consumer не коммитит | | STALL | Lag не меняется | | REWIND | Offset moved backwards |

3. Prometheus + kafka_exporter

# docker-compose.yml
services:
  kafka-exporter:
    image: danielqsj/kafka-exporter
    command:
      - '--kafka.server=kafka:9092'
    ports:
      - "9308:9308"

  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml

  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
# prometheus.yml
scrape_configs:
  - job_name: 'kafka'
    static_configs:
      - targets: ['kafka-exporter:9308']

4. Grafana Dashboard

Ключевые панели:

  • Consumer lag per partition (time series)
  • Total lag across partitions (single stat)
  • Lag rate (derivative — lag growing speed)
  • Consumer throughput (messages/sec)
  • Producer vs Consumer throughput comparison

Таблица типичных ошибок

Ошибка Симптомы Последствия Решение
Мониторинг только total lag Не видно проблемных партиций Одна партиция отстаёт, остальные OK — проблема скрыта Lag per partition
Lag без context Lag = 500 — это хорошо или плохо? Неправильные решения Lag + throughput + trend
Без alerting Lag растёт, никто не видит Consumer не справляется, data delay Alert thresholds
Lag = 0 = OK assumption Lag = 0 но consumer stopped False sense of security Monitor consumer status
Только current lag Без history Невозможно trend analysis Store lag history
Monitoring на producer side Lag consumer metric, не producer Не релевантные данные Monitor consumer lag

Сценарии использования

Scenario 1: Lag растёт линейно

Time     Lag
10:00    100
10:05    300
10:10    500
10:15    700
→ Linear growth = consumer slower than producer

Root cause:
  - Consumer processing slow (DB bottleneck, external API)
  - GC pauses
  - Network latency to broker

Action: Scale consumers или optimize processing

Scenario 2: Lag spike затем recovery

Time     Lag
10:00    100
10:05    5000   ← spike
10:10    3000
10:15    500    ← recovery
10:20    100

Root cause:
  - Temporary broker unavailability
  - Network partition resolved
  - GC pause ended

Action: Investigate spike cause, may not need scaling

Scenario 3: Lag растёт экспоненциально

Time     Lag
10:00    100
10:05    200
10:10    800
10:15    3200
10:20    12800
→ Exponential = consumer completely stuck

Root cause:
  - Poison pill message
  - Deadlock in consumer
  - External dependency down

Action: URGENT — find root cause, may need to skip messages

Когда НЕ использовать lag как единственную метрику

  • Lag = 0 но consumer processing медленно — может быть spike при burst
  • Lag высокий но decreasing — consumer catch up, не паниковать
  • Lag без producer rate — lag может быть высоким при low producer activity
  • Lag без error rate — lag может расти из-за ошибок, не slow processing

🔴 Senior Level

Глубокие внутренности

Lag calculation internals

Kafka broker:
  Partition State:
    - LogEndOffset: offset next message will get
    - HighWatermark: offset of last committed message (all ISR)
    - LastStableOffset: for transactional messages

Consumer:
  CommittedOffset: stored in __consumer_offsets topic
  Position: next offset to be read (may be ahead of committed)

Lag (per partition) = LogEndOffset - CommittedOffset

__consumer_offsets topic:
  - Internal Kafka topic
  - Compact cleanup (only latest offset per group+partition)
  - 50 partitions (default)
  - Key: [group.id, topic, partition]
  - Value: {offset, metadata, leaderEpoch, timestamp}

Offset Commit Lag vs Processing Lag

Offset Commit Lag:
  CommittedOffset = 800
  ProcessingOffset = 850  (processed but not committed)
  LogEndOffset = 1000
  
  "Visible lag" = 1000 - 800 = 200
  "Real lag" = 1000 - 850 = 150
  
  Разница = 50 messages in-flight (processed, not committed)

Это важно для monitoring:
  - Burrow видит committed offset → lag = 200
  - Реальный processing lag = 150
  - In-flight messages не видны external tools

Kafka Exporter Internals

kafka_exporter (Prometheus):
  1. AdminClient.ListConsumerGroupOffsets() → committed offsets
  2. AdminClient.ListOffsets(latest) → log end offsets
  3. Calculates lag
  4. Exposes as Prometheus metrics

Metrics exposed:
  kafka_consumergroup_lag{group="order-service",topic="orders",partition="0"}
  kafka_consumergroup_lag_sum{group="order-service",topic="orders"}
  kafka_topic_partition_current_offset{topic="orders",partition="0"}
  kafka_consumergroup_current_offset{group="order-service",topic="orders",partition="0"}

Scrape interval: 10–30s (не чаще — expensive admin calls)

Trade-offs

Подход Точность Overhead Real-time Complexity
CLI (kafka-consumer-groups) Высокая Ручной Point-in-time Низкая
Burrow Высокая Низкий ~5s delay Средняя
kafka_exporter + Prometheus Средняя Низкий ~10s delay Средняя
JMX (kafka.consumer) Высокая Средний Real-time Высокая
Custom monitoring Максимальная Зависит Real-time Высокая

Edge Cases

1. Lag = 0 но consumer не работает:

Сценарий:
  Consumer processed up to offset 1000
  No new messages produced за последние 10 мин
  Lag = LogEndOffset(1000) - CommittedOffset(1000) = 0
  
 看起来 OK, но consumer может быть dead!

Решение:
  1. Monitor consumer heartbeat (session.timeout.ms)
  2. Monitor processing rate (messages/sec)
  3. Monitor "time since last commit"
  4. Burrow status: STOP если нет heartbeat

2. Lag spike при rebalancing:

Сценарий:
  10:00: Consumer-1 обрабатывает partition 0, lag = 100
  10:01: Consumer-2 joins → rebalancing
  10:02: Partition 0 assigned to Consumer-2
  10:02: Lag spike: 100 → 500 (no processing during rebalance)
  10:05: Consumer-2 catch up, lag = 50

Это expected behavior!
  - Lag spike во время rebalance = normal
  - Lag recovery after rebalance = healthy

Monitoring:
  Ignore lag spikes < 5 min duration
  Alert on lag spike > 10 min without recovery

3. Consumer rewind (lag sudden increase):

Сценарий:
  Committed offset: 1000
  Next poll: committed offset = 500 (rewind!)
  Lag suddenly: 500 → 1500

Root causes:
  1. Offset reset (auto.offset.reset = earliest)
  2. Manual seek (consumer.seek)
  3. Offset data loss (__consumer_offsets corruption)
  4. Consumer group reset

Burrow status: REWIND
Action:
  Investigate — rewind может быть intentional (replay)
  Или data loss (critical!)

4. Transactional messages и Lag:

Сценарий:
  Producer начал transaction
  Записал messages offsets 1000-1050
  Transaction NOT committed yet
  LogEndOffset = 1051
  LastStableOffset = 1000

Consumer lag calculation:
  Если consumer isolation.level = read_committed:
    Видит только committed messages до offset 1000
    Lag = 1000 - committed_offset  (correct)
  Если consumer isolation.level = read_uncommitted:
    Видит все messages включая uncommitted
    Lag = 1051 - committed_offset  (misleading!)

Решение: monitoring должен использовать LastStableOffset
         для read_committed consumers

5. Multi-cluster lag:

Сценарий:
  MirrorMaker 2 replicates: Cluster A → Cluster B
  Consumer читает из Cluster B
  
  Lag на Cluster B может расти из-за:
  1. Consumer slow (normal lag)
  2. Replication lag (Cluster A → Cluster B slow)
  3. Producer slow на Cluster A

Нужно monitor:
  - Consumer lag на Cluster B
  - Replication lag (A → B)
  - Producer lag на Cluster A

Только тогда можно определить root cause

Производительность (production numbers)

Metric Normal Warning Critical
Lag per partition < 1000 1000–10000 > 10000
Lag growth rate < 100/min 100–1000/min > 1000/min
Consumer throughput > 90% of producer 50–90% < 50%
Commit latency (P99) < 50ms 50–200ms > 200ms
Time to recover lag < 5 min 5–30 min > 30 min

Production War Story

Ситуация: Delivery platform, 500K events/min, 20 partitions, 10 consumers.

Проблема: В 2 AM lag начал расти: 1000 → 50000 за 30 минут. К 3 AM — 200000 lag. On-call engineer получил alert, начал investigation. К 4 AM — 500000 lag, 1 час backlog.

Investigation timeline:

02:00 — Alert: lag > 10000
02:05 — On-call checks Grafana: lag growing exponentially
02:10 — Consumer throughput dropped: 50K → 5K msg/min
02:15 — No consumer restarts, no rebalancing
02:20 — DB connection pool exhausted (root cause!)
02:25 — DB team: migration running, connection limit reached
02:30 — Migration stopped, connections freed
02:35 — Consumers catch up
03:30 — Lag back to 0

Root cause: DB team ran migration без уведомления. Migration использовал все connections. Consumers не могли писать в БД → processing stopped → lag grew.

Lessons:

  1. Lag alert сработал — GOOD
  2. Но lag не сказал WHY — нужен DB connection monitoring
  3. Cross-team communication process needed
  4. Lag growth rate alert (> 10K/min) — более actionable
  5. Runbook для lag investigation — критично

Post-mortem improvements:

Alerts added:
  - lag > 10K for 5m → warning
  - lag > 100K for 2m → critical
  - lag growth rate > 10K/min → critical
  - consumer throughput < 50% of producer → warning
  - DB connection pool > 90% → critical

Runbook created:
  1. Check lag per partition (is it all or one?)
  2. Check consumer throughput vs producer rate
  3. Check consumer logs for errors
  4. Check external dependencies (DB, API)
  5. Check recent deployments

Monitoring (JMX, Prometheus, Burrow)

JMX метрики

kafka.consumer:type=consumer-fetch-manager-metrics,client-id=consumer-1
  - records-lag-max: max lag across all partitions
  - records-lag-avg: average lag
  - fetch-latency-avg: time to fetch from broker
  - fetch-rate: fetch requests per second

kafka.consumer:type=consumer-coordinator-metrics,client-id=consumer-1
  - commit-latency-avg
  - commit-rate
  - last-heartbeat-seconds-ago

kafka.server:type=ReplicaManager,name=UnderReplicatedPartitions
  (если partitions under-replicated → consumer may see stale data)

Prometheus + Grafana Dashboard

# Recording rules
- record: kafka_consumer_lag_rate
  expr: deriv(kafka_consumergroup_lag[5m])

- record: kafka_consumer_throughput
  expr: rate(kafka_consumergroup_current_offset[5m])

- record: kafka_producer_throughput
  expr: rate(kafka_topic_partition_current_offset[5m])

# Alerts
- alert: KafkaConsumerLagHigh
  expr: kafka_consumergroup_lag_sum > 10000
  for: 5m
  labels:
    severity: warning
  annotations:
    summary: "Consumer lag is high (> 10K)"

- alert: KafkaConsumerLagGrowing
  expr: kafka_consumer_lag_rate > 1000
  for: 5m
  labels:
    severity: critical
  annotations:
    summary: "Consumer lag growing at /min"

- alert: KafkaConsumerThroughputLow
  expr: kafka_consumer_throughput / kafka_producer_throughput < 0.5
  for: 10m
  labels:
    severity: warning
  annotations:
    summary: "Consumer throughput < 50% of producer throughput"

Burrow Configuration

# burrow.ini
[consumer.order-service]
class-name=kafka
cluster=production
group=order-service

# Burrow evaluation
[lagcheck.order-service]
group-order-service.orders.0=OK
group-order-service.orders.1=OK
group-order-service.orders.2=OK

# Notification
[notifier.default]
class-name=email
threshold=1
send-close=true
template=lag-email.tmpl

# HTTP endpoint
[httpserver.default]
address=:8000

Burrow evaluation statuses:

OK:      Lag decreasing or 0
WARN:    Lag stable but above threshold
ERROR:   Lag increasing
STOP:    Consumer stopped committing
STALL:   Lag not changing (consumer stuck)
REWIND:  Offset moved backwards

Highload Best Practices

✅ Lag per partition monitoring (не только total)
✅ Lag growth rate monitoring (deriv/lag rate)
✅ Lag + throughput + producer rate together
✅ Burrow status monitoring (OK/WARN/ERROR/STOP/STALL)
✅ Alert thresholds по severity (warning, critical)
✅ Runbook для lag investigation
✅ Auto-scaling на lag thresholds (KEDA, custom HPA)
✅ Historical lag data (trend analysis)
✅ Multi-cluster lag (replication lag)
✅ Transactional message awareness (LastStableOffset)

❌ Lag без context (throughput, producer rate)
❌ Lag без alerting (useless metric)
❌ Lag total без per-partition breakdown
❌ Lag monitoring без runbook
❌ Lag без historical data (no trend analysis)
❌ Lag на production без auto-scaling plan
❌ Lag monitoring без consumer status (dead consumer, lag=0)

Auto-scaling на основе lag (KEDA)

# KEDA ScaledObject для auto-scaling Kafka consumers
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: kafka-consumer-scaler
spec:
  scaleTargetRef:
    name: order-consumer-deployment
  minReplicaCount: 3
  maxReplicaCount: 20
  triggers:
    - type: kafka
      metadata:
        bootstrapServers: kafka:9092
        consumerGroup: order-service
        topic: orders
        lagThreshold: "5000"    # scale up when lag > 5000
        activationLagThreshold: "1000"  # activate when lag > 1000
        offsetRestPolicy: "latest"
        allowIdleConsumers: "false"

Архитектурные решения

  1. Lag — leading indicator — растёт до того как users impacted
  2. Lag + throughput + error rate — three pillars of consumer health
  3. Burrow status > lag number — status trend более actionable чем absolute lag
  4. Auto-scaling on lag — proactive scaling, not reactive
  5. Lag per partition — partition-level visibility critical

Резюме для Senior

  • Lag = LogEndOffset - CommittedOffset, но нюансы (in-flight, transactional)
  • Burrow status (OK/WARN/ERROR/STOP/STALL/REWIND) более информативен чем lag value
  • Lag growth rate — более actionable чем absolute lag
  • Lag spike при rebalancing — normal, не alert
  • Lag = 0 не значит consumer healthy (check heartbeat, throughput)
  • Transactional messages — use LastStableOffset для lag calculation
  • Auto-scaling on lag thresholds — KEDA для Kubernetes
  • Production runbook для lag investigation — обязателен
  • Lag per partition — total lag hides partition-level issues
  • Multi-cluster: consumer lag + replication lag + producer lag — все три нужны

🎯 Шпаргалка для интервью

Обязательно знать:

  • Consumer Lag = LogEndOffset - CommittedOffset — ключевая метрика здоровья консьюмера
  • Lag per partition критичен: total lag скрывает проблемные партиции (hot key)
  • Burrow status: OK, WARN, ERROR, STOP, STALL, REWIND — информативнее чем absolute lag
  • Lag growth rate (deriv) — более actionable чем absolute lag value
  • Lag = 0 не значит healthy: consumer может быть stopped, нет новых сообщений
  • Инструменты: kafka-consumer-groups.sh, Burrow, Prometheus + kafka_exporter, Grafana
  • Auto-scaling на lag thresholds: KEDA для Kubernetes

Частые уточняющие вопросы:

  • Lag=0 — это всегда OK? — Нет, consumer может быть dead; check heartbeat и throughput.
  • Что значит REWIND в Burrow? — Offset moved backwards (replay, reset, или data loss).
  • Lag spike при rebalancing — это проблема? — Нет, expected behavior; ignore < 5 min duration.
  • Как определить hot partition? — Lag imbalance across partitions: одна отстаёт, другие OK.

Красные флаги (НЕ говорить):

  • «Lag=0 значит всё отлично» — consumer может быть stopped
  • «Только total lag достаточно» — скрывает partition-level problems
  • «Lag spike при ребалансе — критическая проблема» — normal behavior
  • «Burrow не нужен, хватит CLI» — CLI = point-in-time, Burrow = trend analysis

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

  • [[12. Что такое offset в Kafka]]
  • [[13. Как работает commit offset]]
  • [[24. Как обрабатывать ошибки при чтении сообщений]]
  • [[25. Что такое DLQ (Dead Letter Queue)]]