Watch it run
Play it or step through. Every step says what just happened.
const log = ["o1", "o2", "o3", "o4", "o5"];let committed = 0; // kept by the group coordinatorconst poll = (from, max) => log.slice(from, from + max).map((v, i) => ({ offset: from + i, v })); // Consumer A owns the partition: process a batch, then commitlet batch = poll(committed, 3);for (const r of batch) console.log("A processed", r.offset, r.v);committed = batch[batch.length - 1].offset + 1; // A processes the next batch but crashes before committingbatch = poll(committed, 3);for (const r of batch) console.log("A processed", r.offset, r.v); // Rebalance: B gets the partition and resumes from the commitbatch = poll(committed, 3);for (const r of batch) console.log("B processed", r.offset, r.v);committed = batch[batch.length - 1].offset + 1;console.log("committed", committed);Partition 0
The log; reading never removes records
Consumer A
Records it has fetched
Consumer B
Records it has fetched
Committed offset
Next offset to read, stored by the group
Console0 lines
Nothing has run yet. One partition, a group with consumers A and B, and A owns the partition.
A model of one partition, not the Kafka client API. In Java the commit is consumer.commitSync() after the processing loop.
A model of one partition, not the Kafka client API. In Java the commit is consumer.commitSync() after the processing loop.
Try it yourself
Change the code, predict the order, then run it for real in your browser.
Press Run to see what your code prints, in order.
Runs in this browser, in an isolated frame with no access to this page. console.log, info, warn and error are shown; code runs as a classic script, so use an async function for await.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.
On a real cluster
Two console consumers in group shipping on a three-partition topic, using the new protocol (group.protocol=consumer):
bin/kafka-consumer-groups.sh --bootstrap-server kafka1:9092 --describe --group shipping --members --verboseGROUP CONSUMER-ID CLIENT-ID #PARTITIONS CURRENT-EPOCH CURRENT-ASSIGNMENT ...shipping CNZp_LDPRGCdXHKzF638Mw worker-1 2 2 orders:0,1shipping WUzGEJhgTdyzZpJbweOUeg worker-2 1 2 orders:2The group's state showed the server-side assignor uniform. Describing a group without --members shows lag per partition, the gap between the committed offset and the end of the log:
GROUP TOPIC PARTITION CURRENT-OFFSET LOG-END-OFFSET LAGbilling orders 0 3 4 1billing orders 1 1 4 3--reset-offsets --to-earliest (or --to-datetime, --shift-by) moves a stopped group's position, which is how you replay.
Settings that cause trouble
enable.auto.commit=true(default) commits everyauto.commit.interval.ms(5 s) whatever poll() has returned, done or not.max.poll.interval.ms(5 minutes): a member that does not call poll() within it is considered dead and its partitions are moved. Slow processing per batch looks like a crash. Lowermax.poll.records(default 500) or speed up the handler.auto.offset.resetdecides where a group with no committed offset starts:latestby default, so a new group skips history unless you setearliest.- Static membership (
group.instance.id) lets a restarting member keep its partitions within the session timeout instead of triggering two rebalances.
Kafka 4.0 made the new rebalance protocol (KIP-848) generally available. The broker computes assignments and moves partitions incrementally, without stopping the whole group. Clients still default to group.protocol=classic; you opt in with group.protocol=consumer.