Как мониторить lag консьюмера в Kafka
Представьте очередь в магазине. Кассир (consumer) обслужил 80 покупателей, а в очереди стоит ещё 200. Lag = 200 — это сколько покупателей ещё ждут. Если lag растёт — кассир не с...
🟢 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:
- Lag alert сработал — GOOD
- Но lag не сказал WHY — нужен DB connection monitoring
- Cross-team communication process needed
- Lag growth rate alert (> 10K/min) — более actionable
- 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"
Архитектурные решения
- Lag — leading indicator — растёт до того как users impacted
- Lag + throughput + error rate — three pillars of consumer health
- Burrow status > lag number — status trend более actionable чем absolute lag
- Auto-scaling on lag — proactive scaling, not reactive
- 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)]]