Kafka rebalance búrky: prečo scale-out consumerov vie zhoršiť lag
Toto je jedna z najprotiintuitívnejších Kafka situácií:
- consumer lag rastie
- zvýšite počet consumerov 20 → 40
- lag začne rásť ešte rýchlejšie
- throughput padne
- v logoch stále dokola “revoking partitions” / “rebalancing”
Ja som si to raz spôsobil sám — dobrý úmysel, zlý výsledok. Systém nedostal viac kapacity. Dostal viac rebalanceov a rebalance je pre consumption v praxi stop-the-world event.
Testované na: Apache Kafka 3.6–3.8, Java consumeri 3.6–3.8, high-throughput topic + DB-backed processing.
Incident (anonymizovaný)
Pipeline: Kafka → consumer → Postgres write. Jeden deploy omylom zvýšil per-message prácu (o jednu DB query navyše). Spracovanie spomalilo. Lag rástol.
Zvýšil som počet consumerov. Zhoršilo to:
- DB pool začal thrashovať
- processing time per poll rástol
- consumeri prekračovali
max.poll.interval.msalebo nestíhali heartbeat - group sa začal rebalanceovať skoro nonstop
- každý rebalance odoberal partitions, prerušoval prácu a generoval duplicity (nemali sme ešte 100% idempotenciu)
Blast radius: oneskorené spracovanie eventov + duplicity + tlak na downstream.
Constraint: potrebovali sme stabilizovať group rýchlo, nie “len pridať nody”.
Timeline
- T-0: alert na lag; processing latency ide hore.
- T+10m: scale-out consumerov; lag rastie rýchlejšie.
- T+20m: v logoch časté rebalancy a revokácie partitions.
- T+30m:
kafka-consumer-groups.shukáže osciláciu medzi STABLE a REBALANCING. - T+45m: mitigácia: znížiť prácu per poll, doladiť config, zmeniť assignor, pridať static membership.
- T+90m: group sa stabilizuje; lag začne klesať.
- T+1d: pridáme “rebalance budgety” a idempotency guardrails.
Mechanizmus: prečo rebalance zničí throughput
Rebalance zastaví consumption
Počas rebalance sa partitions odoberú a pridelia znova. Podľa clienta/assignoru to môže byť dosť brutálne:
- consumeri prestanú fetchovať
- in-flight processing sa preruší alebo zopakuje
- cache sa warmuje znova
- commity failujú
Ak sa rebalance deje stále, efektívny čas consumptionu ide k nule.
Dva najčastejšie spúšťače
Spúšťač 1: prekročené max.poll.interval.ms
Ak appka nevolá poll() dosť často (lebo spracovanie je pomalé alebo blokované), Kafka ho vyhodí z group → rebalance.
Spúšťač 2: heartbeat/session timeout
GC pauzy, sieť, alebo preťaženie spôsobí missed heartbeats.
Scale-out to vie zhoršiť, lebo:
- zvyšuje DB contention (spracovanie trvá dlhšie)
- zvyšuje churn počas deployov
- zvyšuje pravdepodobnosť, že aspoň jeden člen je v danom momente “pomalý”
Runbook: potvrďte rebalance storm
Čo skontrolovať ako prvé
- Consumer logy Hľadajte:
- “Revoked partitions”
- “Rebalance in progress”
- “Max poll interval exceeded”
- “Commit failed: rebalance in progress”
- Group state
kafka-consumer-groups.sh --bootstrap-server <broker:9092> --describe --group <group>
kafka-consumer-groups.sh --bootstrap-server <broker:9092> --describe --group <group> --members
Hľadám:
- group nie je stabilne STABLE
- members flappujú
- partitions sa presúvajú stále dokola
- Tvar lagu “Píla” (rast → krátky pokles → rast) často sedí na rebalance loop.
Ako potvrdiť hypotézu
A. Či spracovanie blokuje poll()
Najčastejšia chyba je robiť ťažkú prácu na poll threade.
Ak v incidente nevidíte kód, dá sa to vyčítať:
- veľký processing time per batch
- oneskorené commity
- logy o prekročení max.poll
B. Či je skutočný bottleneck downstream (DB) U nás to bol DB pool.
Ak máte:
- metriky poolu
- DB latenciu
- CPU nie je vysoké, ale throughput je nízky
Bezpečné mitigácie (poradie)
-
Prestaňte zväčšovať group Nescaleujte ďalej, kým nie je STABLE.
-
Znížiť prácu per poll
- znížiť
max.poll.records - presunúť spracovanie do worker poolu (poll thread nech stále polluje)
- backpressure: nepoluj, ak je interná práca queue plná
-
Zvýšiť
max.poll.interval.ms(opatrne) Dá to čas pomalému spracovaniu bez vyhodenia z group. -
Cooperative rebalancing + static membership Zníži stop-the-world rebalancy pri deployoch a flappingu.
Rizikové mitigácie
- extrémne veľké timeouty → pomalý failover pri reálnom páde
- rozbitá disciplína commitov → duplicity a data loss
- agresívne retries bez idempotencie
Čo sme zmenili (konkrétne config diffs)
Predtým (príliš optimistické):
enable.auto.commit=true
max.poll.records=500
max.poll.interval.ms=300000
session.timeout.ms=10000
heartbeat.interval.ms=3000
partition.assignment.strategy=org.apache.kafka.clients.consumer.RangeAssignor
Potom (stabilnejšie, deploy-friendly):
enable.auto.commit=false
max.poll.records=50
max.poll.interval.ms=1800000
session.timeout.ms=30000
heartbeat.interval.ms=10000
partition.assignment.strategy=org.apache.kafka.clients.consumer.CooperativeStickyAssignor
group.instance.id=${HOSTNAME}
V kóde sme zabezpečili:
- poll thread iba polluje a enqueueuje prácu
- worker pool spracuje recordy
- offset commit až po úspešnom spracovaní
- idempotentné správanie (alebo idempotency keys)
Ako verifikovať
-
Group ostane STABLE Spustite
--describe --membersviackrát počas 10–15 min. Membership nesmie flappovať. -
Rebalance frekvencia padne V logoch majú byť revokácie zriedkavé (typicky iba počas deploy okna).
-
Lag klesá monotónne Po stabilizácii má lag trendovať dole. Ak osciluje, stále máte nestabilitu.
-
Downstream tlak sa zlepší DB pool prestane thrashovať, latencie sa stabilizujú.
Prevencia / guardrails
- Rebalance budget
- alert ak rebalancy prekročia N/hod
- Poll-time budget
- maximálny čas spracovania na poll cyklus
- Idempotency kontrakt
- duplicity sa v niektorých failure modoch dejú; spravte ich safe
- Backpressure kontrakt
- consumer musí prestať polovať, keď je downstream saturovaný
Súvisiace čítanie
- Jedna partition na 99% CPU: Zastav Kafka hotspoty skôr ako dorazia do produkcie
- Idempotencia API: Ako navrhnúť endpointy odolné voči retry
- Transactional Outbox: Ako vyriešiť Dual Write problém bez 2PC
- Protobuf evolúcia v eventoch: Prečo buf breaking nestačí
- Polia zmizli ale nič nespadlo: Zachyť Schema Evolution bugy pred produkciou
- Vyčerpanie Connection Poolu: Tichý Spúšťač Výpadkov
- Connection Pool Sizing s Little’s Law: Matematický Prístup k HikariCP a PgBouncer
Súvisiace články
Redis AOF fsync latency špičky: keď sa durabilita stane tvojím p99
Redis AOF vie spraviť z durability p99 špičky: fsync tlak a BGREWRITEAOF fork CoW. Runbook na dôkaz, bezpečné mitigácie a guardrails.
Pod zaseknutý v Terminating: produkčný rozhodovací strom pre finalizery, volume a mŕtve nody
Konzervatívny runbook na bezpečné odblokovanie Terminating Podov: finalizery, CSI/volume cleanup, mŕtve nody a kedy (a ako) použiť force delete.
PostgreSQL XID wraparound: núdzový playbook pre vacuum freeze v incidente
PostgreSQL môže prejsť do read-only pri XID wraparound. Núdzový playbook: nájsť najstaršie tabuľky, odblokovať vacuum freeze a prevencia do budúcna.
ingress-nginx reload búrky: prečo 502 špičky sedia s Ingress churnom
Reloady NGINX Ingressu vedia dropovať keep-alive a robiť 502 špičky pri častých zmenách. Runbook na dôkaz reloadu, zníženie churnu a hardening.
Citujte tento článok
Ak na článok odkazujete, pridajte pôvodnú URL a uveďte autora.