Chapter 52 · Managing Amazon Msk
Subchapter 52.9
references/troubleshoot-consumer-lag.mdMarkdown11 KBView on GitHub
Check SumOffsetLag (DEFAULT level, dimensions: Cluster Name, Consumer Group, Topic) and OffsetLag (PER_TOPIC_PER_PARTITION level, dimensions: Consumer Group, Topic, Partition).
Decision tree:
Check these broker metrics (PER_BROKER level):
CpuUser + CpuSystem > 60%: Broker is overloaded. See troubleshoot-performance.md.ProduceTotalTimeMsMean elevated: Produce latency is high, slowing replication and consumer fetches.FetchConsumerTotalTimeMsMean elevated: Consumer fetch requests are slow at the broker.RequestHandlerAvgIdlePercent < 30%: Request handlers saturated — check client batch sizes before scaling. See troubleshoot-performance.md Step 3.NetworkProcessorAvgIdlePercent < 30%: Network threads saturated. May indicate connection storms, high TLS overhead, or too many small requests.Standard-specific checks (skip for Express):
VolumeQueueLength elevated or VolumeTotalWriteTime increasing: EBS throughput saturated. Calculate BytesInPerSec × RF vs volume throughput ceiling (250 MiB/s default for GP2/GP3). See troubleshoot-performance.md Step 4.BwInAllowanceExceeded or BwOutAllowanceExceeded > 0: EC2 network bandwidth exceeded — traffic shaping active. Check per-broker traffic distribution for AZ skew. See troubleshoot-performance.md Step 5.HeapMemoryAfterGC > 60%: Memory pressure after GC. High connection count, excessive consumer groups, or high partition count can drive this. Reduce transactional.id.expiration.ms from 7 days to 1 day as a quick win.BurstBalance dropping toward 0: GP2 volume I/O burst credits depleting under sustained load. Consider provisioned throughput (GP3) or a larger instance type.Express-specific checks:
ProduceThrottleTime or FetchThrottleTime > 0: Per-broker throughput quota exceeded. Scale to a larger Express broker size or add brokers.If all broker metrics are healthy, the issue is client-side — go to Step 4.
When lag is isolated to specific partitions while others have zero lag, the cause is typically:
Confirm with: PER_TOPIC_PER_PARTITION level OffsetLag — check which partitions have growing lag. Use kafka-consumer-groups.sh --describe --group <group-id> to see per-partition lag alongside which consumer owns each partition. Compare per-topic-per-broker BytesInPerSec to see if specific brokers receive disproportionate data for that topic.
Important: Adding partitions does NOT fix key skew — each hot key still hashes to exactly one partition, so the disproportionate load from high-volume keys remains concentrated regardless of how many partitions exist. You must fix the key distribution itself.
Fix options:
If max.poll.interval.ms is exceeded, the consumer is kicked from the group, triggering a rebalance. Check the consumer application for:
max.poll.records too high for the processing time availableFix: Reduce max.poll.records, optimize processing logic, or increase max.poll.interval.ms (not recommended as a first option — it masks the real problem).
If the number of consumers in the group is less than the number of partitions, some consumers handle multiple partitions and may not keep up. Check consumer group membership via Kafka CLI (requires direct broker connectivity):
kafka-consumer-groups.sh --bootstrap-server <bootstrap> --describe --group <group-id>Look at the LAG column per partition and the CONSUMER-ID column to see which consumers are overloaded.
Poor fetch settings can cause the consumer to make excessive small requests, wasting broker resources and slowing the consumer loop:
fetch.min.bytes too low (default 1 byte): Every fetch returns immediately even with minimal data, generating high request rates. Set to at least 1 KB; 32-128 KB for throughput workloads.fetch.max.wait.ms too low: Broker returns partial fetches too quickly. Recommend 1000ms.fetch-rate and records-consumed-rate metrics. A high fetch-rate with low records-consumed-rate indicates inefficient fetching.If isolation.level=read_committed is set, the consumer only reads up to the Last Stable Offset (LSO). A hanging transaction from a crashed or misconfigured transactional producer will prevent the LSO from advancing, causing lag to grow indefinitely on affected partitions — even if the current producers are non-transactional. Common causes: a Kafka Streams app with processing.guarantee=exactly_once_v2 that crashed, or a previous producer version that set transactional.id and was decommissioned without cleanly aborting its transactions. Fix: If no producers actively use transactions, set isolation.level=read_uncommitted. Otherwise, check for hanging transactions — see below.
If OffsetLag grows on partitions of __consumer_offsets, a hanging transaction may block offset commits. Detect with Kafka CLI:
kafka-transactions.sh --bootstrap-server <bootstrap> find-hanging --broker-id <broker-id>Abort hanging transactions:
kafka-transactions.sh --bootstrap-server <bootstrap> abort --topic __consumer_offsets --partition <partition> --start-offset <offset>MSK performs rolling broker restarts during patching (Standard brokers enter MAINTENANCE state; Express brokers stay ACTIVE).
Confirm maintenance is in progress: Run aws kafka describe-cluster-v2 --cluster-arn <arn> and check ClusterState. A value of MAINTENANCE (Standard) or UPDATING confirms an operation is underway. Express clusters stay ACTIVE during maintenance, so check the MSK console or EventBridge for maintenance notifications.
Symptoms during maintenance (Standard):
UnderReplicatedPartitions spikes then gradually decreases (Standard only — Express does not emit this metric)ActiveControllerCount changes (controller election)Symptoms during maintenance (Express):
UnderReplicatedPartitions metric available — cannot use URP to track progressProduceThrottleTime or FetchThrottleTime may briefly spike if remaining brokers absorb extra loadActiveControllerCount may briefly fluctuateThis is expected and self-resolving. Do NOT:
Consumer resilience configuration to minimize maintenance impact — see configure-clients.md:
session.timeout.ms = 45000 (or 60000)heartbeat.interval.ms = 10000 (or 15000)partition.assignment.strategy = CooperativeStickyAssignorgroup.instance.id set to a unique value per consumer instanceSymptoms: Consumer group state alternates between Stable and PreparingRebalance. rebalance-latency-avg client metric is elevated. Lag grows during each rebalance cycle.
Common causes:
session.timeout.ms too low: Default 10000ms (10s) is too short — GC pauses, network blips, or slow consumer startup can cause false evictions, triggering unnecessary rebalances.group.initial.rebalance.delay.ms (broker-side config) to match your average deployment time to batch rebalances.kafka-consumer-groups.sh --bootstrap-server <bootstrap> --list | wc -l. Excessive consumer groups overload the group coordinator broker.PreparingRebalance. This bug does not affect Kafka 3.x+ or Express brokers. Mitigation: identify the coordinator broker and restart it. Before restarting any broker, verify UnderReplicatedPartitions == 0 — restarting during URP risks data loss.Fix:
CooperativeStickyAssignor to enable incremental rebalances instead of stop-the-world. Migration requires two rolling restarts: first deploy with partition.assignment.strategy=RangeAssignor,CooperativeStickyAssignor, then remove RangeAssignor in a second deployment. Mixing eager and cooperative protocols in the same group causes InconsistentGroupProtocolException.group.instance.id for static group membership — consumers can rejoin after brief disconnects without triggering a full rebalance.session.timeout.ms = 45000-60000 and heartbeat.interval.ms = 10000.group.initial.rebalance.delay.ms (broker-side) to match deployment rollout time.consumer.close() on SIGTERM for clean group leave instead of relying on session timeout.To identify the coordinator broker:
kafka-consumer-groups.sh --bootstrap-server <bootstrap> --describe --group <group-id> --stateThe output shows the coordinator. If the group is stuck in PreparingRebalance on older Kafka versions, restarting the coordinator broker can unblock it.