Runbook: Kafka consumer lag

Severity (default): P2 (P1 для consumer’а на критическом пути pricing/search). Owner: on-call. Связанные алерты:

  • kafka_consumer_lag{group="<name>"} > 10000 за 5 мин.
  • kafka_consumer_lag не уменьшается > 30 мин.

Симптом

  • Алерт по lag.
  • Stale данные в проекциях (ES не отражает свежие изменения; CH аналитика отстаёт).
  • Признаки каскада: outbox_lag_seconds тоже растёт (если consumer находится downstream от outbox).

Диагностика

# 1. Какие группы лагают?
kubectl -n tracium exec deploy/kafka-tools -- \
  kafka-consumer-groups.sh --bootstrap-server kafka:9092 \
  --describe --all-groups | sort -k6 -n -r | head -30
# 2. Состояние подов consumer'а
kubectl -n tracium get pods -l app=<consumer-service>
kubectl -n tracium top pods -l app=<consumer-service>
# 3. Логи consumer'а — есть ли panic / повторяющаяся ошибка?
kubectl -n tracium logs -l app=<consumer-service> --since=15m --tail=500 \
  | grep -E "ERROR|panic|FATAL"
# 4. Состояние топика
kubectl -n tracium exec deploy/kafka-tools -- \
  kafka-topics.sh --bootstrap-server kafka:9092 \
  --describe --topic <topic_name>

Возможные причины:

  • A: Consumer упал / убит OOM.
  • B: Медленный consumer (спикнула latency десериализации / записи в БД).
  • C: Hot partition (перекос в partition_key).
  • D: Пауза в downstream (PG / ES недоступен).
  • E: Плохое сообщение poison-pill (consumer бесконечно ретраит).

Смягчение

A: scale + restart

kubectl -n tracium rollout restart deploy/<consumer-service>
# затем
kubectl -n tracium scale deploy/<consumer-service> --replicas=$(($(kubectl -n tracium get deploy/<consumer-service> -o jsonpath='{.spec.replicas}')+2))

B: проверить latency downstream

# Медленные запросы в PG
kubectl -n tracium exec deploy/pg -- psql -c "
  SELECT pid, state, query_start, query
  FROM pg_stat_activity WHERE state != 'idle' ORDER BY query_start LIMIT 20;
"

Снизить batch size consumer’а через config override (env <SERVICE>_KAFKA_MAX_POLL_RECORDS=100), перезапустить.

C: hot partition

Проверить распределение:

kubectl -n tracium exec deploy/kafka-tools -- \
  kafka-consumer-groups.sh --bootstrap-server kafka:9092 \
  --describe --group <group> | sort -k6 -n -r

Если одна партиция держит большую часть lag — проверить partition_key в producer (ADR-0020 §“Гарантии и ordering”).

D: downstream недоступен

Сначала восстановить downstream (соответствующий runbook). Consumer догонит после.

E: poison pill

# Найти offset, на котором consumer застрял
kubectl -n tracium logs deploy/<consumer-service> --since=5m | grep -E "offset=[0-9]+" | tail -5

Опции:

  1. Перенести offset вперёд (skip): kafka-consumer-groups.sh --reset-offsets --to-offset <N+1> --topic <T> --group <G> --execute. Только с явным согласованием — мы пропускаем сообщение.
  2. Если сообщение — подтверждённый poison — отправить в DLQ вручную.

Устранение root cause

  • A: настройка limits/requests для consumer’а; предсказание OOM по бенчмаркам.
  • B: оптимизация downstream (индексы, batch insert).
  • C: пересмотр partition_key в producer’е, увеличение числа партиций.
  • D: более ранние алерты на доступность downstream.
  • E: лучший error handling в consumer’е (skip → DLQ автоматически после N попыток).

Эскалация

  • 30 мин lag не уменьшается → #oncall-data.
  • 1 ч lag на топиках критического пути (canonical.events.v1, offer.observation.v1) → #oncall-product (клиенты видят stale data).

Связано

  • ADR-0002 (Kafka).
  • ADR-0020 (Outbox).
  • 20-architecture/schemas/events/topics.md
  • runbook event-store-projection-lag.md