Copies and the ISR, what acks=all really waits for, choosing the key and the partition count, rebalances during a deploy, compaction, Kafka Connect, a schema rollout, replay by time, and Kafka against Kinesis, Pub/Sub and Event Hubs.
⏱ 20 min readTopics chapter readerLevel · Streaming & Real-Time
01 · Overview
Start here
⏱ 6 min · Topic 1 of 12
Kafka is the most common log for streaming data. Earlier chapters showed how it works on one good day. This chapter shows how teams run it for years, when brokers fail and code changes.
Interviews for senior roles ask about this part. They want to hear what you set, what it protects, and what it still cannot protect.
What you already know
A topic is a log. A partition is one ordered part of it. Each record has an offset.
The key picks the partition. Order exists only inside one partition.
A consumer group shares the partitions. Each group keeps its own committed offsets.
Lag is the log end offset minus the committed offset.
acks=all and an idempotent producer make writes safer. A Kafka transaction gives exactly-once from Kafka to Kafka.
If a line in this list is new to you, read "Streaming Architecture Fundamentals" and "Delivery Guarantees & Ordering" first.
What you will learn
How copies of a partition keep data safe, and when they do not.
How to choose the key and the number of partitions.
Why a deploy can stop a whole consumer group, and how to stop that.
Compaction: keeping the latest value for each key.
Kafka Connect: moving data in and out without writing a consumer.
How to roll out a schema change to many consumers.
How to replay a few hours for one group only.
How Kafka compares with Kinesis, Pub/Sub and Event Hubs.
We use the same shop and the topic orders. All numbers are made up and small, so you can check every row.
02 · Copies
Copies, the ISR and safe writes
⏱ 12 min · Topic 2 of 12
A broker can die. So Kafka keeps copies of each partition on different brokers. The number of copies is the replication factor.
One copy is the leader. Producers write to it and consumers read from it. The other copies are followers, and they copy the leader all the time.
Here the topic orders has replication factor 3, on brokers b1, b2 and b3. For partition p0, b1 is the leader.
The ISR: the copies the leader trusts
A follower that keeps up with the leader is in sync. The list of in-sync copies, with the leader, is the ISR.
A follower that falls behind for longer than replica.lag.time.max.ms leaves the ISR. The default is 30 seconds. It comes back when it catches up.
A consumer can read a record only when every copy in the ISR has it. The first offset that not all of them have is the high watermark.
This table shows p0 at one moment. All three copies are in the ISR:
Partition p0: how far each copy has got (made up)
Broker
Role
Offsets it holds
Log end offset
b1
Leader
0 to 4
5
b2
Follower
0 to 4
5
b3
Follower
0 to 3
4
The smallest log end offset in the ISR is 4, so the high watermark is 4. Consumers can read offsets 0 to 3.
Offset 4 exists on b1 and b2, but consumers wait until b3 has it too. So a consumer never reads a record that a leader change could lose.
What acks=all really waits for
With acks=all, the leader answers "done" when every copy in the ISR has the record. It does not wait for all three copies, only for the ISR.
If the ISR has shrunk to the leader alone, "all" means one copy. The setting min.insync.replicas stops that.
It is the smallest ISR that may accept an acks=all write. Its default is 1. This table shows a topic with min.insync.replicas=2:
In the last row Kafka refuses the write on purpose. One disk is not safe enough, so the producer retries and then reports an error.
With the default of 1, the same write would be accepted on b1 alone. If b1 then failed, the order would be lost.
When every ISR copy is gone
Sometimes every copy in the ISR fails, and only an old follower is left. That follower is missing recent records.
By default Kafka waits, and the partition is offline until an ISR copy comes back. The setting is unclean.leader.election.enable=false.
If you set it to true, the old follower becomes leader. The partition comes back sooner, but the records it never got are gone for good.
Watch out
Replication factor 3 is not three safe copies by itself. Without min.insync.replicas=2, an acks=all write can be acknowledged by one broker. For orders and payments, use replication factor 3, min.insync.replicas=2, acks=all, and keep unclean election off.
Interview question
Interviewers ask what acks=all protects against, and what can still lose data. Answer: it waits for the current ISR, so add min.insync.replicas=2. Data can still be lost by unclean election, or by a producer that gives up after its timeout.
Two choices are hard to undo: the key and the number of partitions. Make them on purpose, before the topic is busy.
The key decides the order
Use the key that must stay in order. If each customer's orders must stay in order, the key is customer_id.
A key gives order for that key and nothing more. Two customers on different partitions have no order between them.
With no key, records are spread over the partitions. That is fine for clicks you only count, but not for changes to one order.
The count decides the speed
Inside one group, one partition is read by one consumer. So the partition count is the most consumers that can work at once.
Size it for the peak you expect later, not for today. This is the arithmetic for the topic orders:
How many partitions for orders? (made-up numbers)
Step
Number
Peak today
20 MB per second
Peak expected in two years
60 MB per second
What one loader can write to the warehouse
5 MB per second
Loaders needed at the future peak: 60 ÷ 5
12
Add room for a bad day
about 16 partitions
Measure "what one loader can do" with a test. The slow part is usually the sink, not Kafka.
Do not choose 1,000 "to be safe". Every partition adds files, copies to send and work when a broker fails.
Adding partitions later moves keys
You can add partitions, but you can never remove them. Adding them changes where most keys go.
Kafka picks the partition as hash(key) mod the number of partitions. To keep it easy, we use customer_id mod the count:
Two partitions become threeworked example
SQL
-- Made-up rule, so you can check each row:-- before: partition = customer_id mod 2-- after: partition = customer_id mod 3-- Real Kafka uses hash(key) mod count. Most keys move in the same way.
Where each customer's new orders go
customer_id
Before (mod 2)
After (mod 3)
Moved?
1
p1
p1
no
2
p0
p2
yes
3
p1
p0
yes
4
p0
p1
yes
5
p1
p2
yes
Four of five customers move. Their old orders stay in the old partition, so new orders can be read before old ones.
Say customer 2 has 1,000 unread orders in p0. Its next order goes to the empty p2 and is read first. For a job that tracks order status, that is wrong data, not just a delay.
If you must grow a keyed topic, pause the producers, let the consumers finish, and then add partitions. Or create a new topic and move to it at a known point.
One key is too hot
A hot key is one key with far more records than the rest. Say customer 9 is a big seller with 30 percent of all orders.
Its partition gets 30 percent of the load, and one consumer cannot keep up. The fix is to split only that key:
Customer 9 now spreads over up to four partitions. Each order still stays in order, because one order_id always goes to the same bucket.
You give up one thing: the orders of customer 9 are no longer in one line. A job that needs a total for customer 9 must add the four buckets.
Interview question
Interviewers ask: "Orders must stay in order per customer, but one customer is 30 percent of the traffic. What do you do?" Answer: keep customer_id as the key, and split only the hot customer by a second field. Then say what order you give up.
A rebalance is when a consumer group shares out its partitions again. It happens when a consumer joins, leaves, or is thought to be dead.
A deploy restarts every consumer, one after another. So a deploy can cause many rebalances.
Here the group loader has 4 pods and 8 partitions, so each pod reads 2 partitions.
Three ways a restart can go
The older way is eager rebalancing. Every pod gives back all its partitions, and the whole group waits for the new plan.
Cooperative rebalancing, since Kafka 2.4, moves only the partitions that must move. The other pods keep reading.
Static membership gives each pod a fixed name, set with group.instance.id. If the pod comes back within session.timeout.ms, it gets its partitions back with no rebalance.
This table compares the three during a deploy of all 4 pods:
A rolling deploy of 4 pods, 8 partitions
Setup
What stops when one pod restarts
Rebalances in the deploy
Eager (the old default)
All 8 partitions, in every pod
8: one when each pod leaves, one when it returns
Cooperative sticky
Only the 2 partitions of that pod
8, but each one is small
Static membership, pod back within 45 seconds
Only the 2 partitions of that pod, while it restarts
0
The default session.timeout.ms is 45 seconds. A pod that takes 2 minutes to start is treated as gone, even with a fixed name.
In Kafka 4.x the default assignor list still uses the eager range assignor. Moving a running group to cooperative takes two rolling deploys.
Kafka 4.0 also added a new group protocol, where the broker plans the assignment. Clients turn it on with group.protocol=consumer.
A slow batch also causes a rebalance
A consumer must call poll() again within max.poll.interval.ms. The default is 5 minutes. If a batch takes longer, Kafka removes the consumer from the group.
Its partitions go to another consumer. That consumer starts from the last committed offset, so the slow batch is read again.
So a group with slow batches keeps rebalancing and writes duplicates. Fix the batch: take fewer records per poll with max.poll.records, or make the sink faster.
Watch out
Raising session.timeout.ms does not fix a slow batch. Heartbeats run on their own thread, so a slow batch trips max.poll.interval.ms instead. Make the write idempotent too, so a rebalance that does happen cannot add duplicates.
Some topics hold the current state of things, such as each customer's address. Old addresses do not matter, but the latest one must stay for ever.
Time-based retention would delete the address of a customer who never moves. Compaction solves this. It keeps at least the latest record for each key and removes older ones.
You turn it on per topic with cleanup.policy=compact. A record with a key and no value is a tombstone. It means "delete this key".
Here is the topic customer_address. Offsets 0 to 4 are in closed segments, the old files of the log. Offset 5 is in the active segment, the file Kafka is writing now:
Offsets 0 and 1 are gone, because newer records replaced them. Offsets keep their numbers, so there are gaps.
Look at c1. It still has two values, Munich and Hamburg, because offset 5 is in the active segment. Compaction happens later, so a reader can still see old values.
The tombstone at offset 4 stays for delete.retention.ms, one day by default. Then it is removed too.
A consumer that is offline longer than that can miss the delete. It would keep customer c2 for ever.
Compacted topics are used for database changes from CDC, for state saved by stream jobs, and by Kafka itself. Committed offsets live in the compacted topic __consumer_offsets.
Watch out
Compaction is not deduplication. It never touches the active segment, and it runs only from time to time. A consumer must still handle several values for one key, and records with no key are refused.
06 · Moving data
Kafka Connect: move data without code
⏱ 11 min · Topic 6 of 12
Most data in Kafka comes from a database or goes to storage. Kafka Connect does this without a consumer you write yourself.
You send it a configuration, and it runs the job. A source connector reads a system into Kafka. A sink connector writes Kafka into a system.
Connect has three parts:
Part
What it is
Worker
A running Connect process. Several workers form one cluster and share the work.
Connector
One configured job, such as "copy the orders database into Kafka".
Task
A thread that moves the data. A connector can split its work into several tasks.
The cluster saves its configurations, offsets and status in three Kafka topics. So if a worker dies, another worker carries on from the saved offset.
A source: Postgres into Kafka with Debezium
Debezium is a set of source connectors for CDC, change data capture. This one reads every change to two Postgres tables:
A Debezium Postgres source connectorworked example
One bad record no longer stops the whole sink. Someone must still watch the dead-letter topic.
This dead-letter topic works only for sink connectors. A source connector fails before a Kafka record exists, so there is nothing to send.
Connect or your own consumer?
Use Connect when
Write your own consumer when
A connector already exists for the system, such as Postgres, S3 or a warehouse.
No good connector exists for the system.
The change per record is small, such as renaming a field.
Each record needs real logic, such as calls to other services.
You want restarts and saved offsets without writing them.
You need full control of batching and of when you commit.
07 · Change
Roll out a schema change
⏱ 10 min · Topic 7 of 12
A schema is the shape of a record: its fields and their types. The chapter "Schema Evolution & Data Contracts" teaches which changes are safe. Here you see how Kafka teams roll one out.
Most teams keep schemas in a Schema Registry. It is a separate service. The Kafka broker never checks schemas, it only stores bytes.
The producer's serializer puts a small schema ID in front of each record. The consumer uses the ID to fetch the schema that wrote the record.
The registry checks a new schema when it is registered. The rule is set per subject, usually one per topic. The rule decides who must upgrade first:
Compatibility rule and upgrade order
Rule
It promises
Upgrade first
BACKWARD (the default)
The new schema can read old records
Consumers
FORWARD
Old schemas can read new records
Producers
FULL
Both
Either side
NONE
Nothing
Plan it by hand
A rule with _TRANSITIVE at the end checks every earlier version, not only the last one.
Here is a real kind of break. The topic orders has 12 consumer teams and the default BACKWARD rule.
The producer team renames the field amount to total, and gives total a default. A rename is a delete plus an add.
The registry accepts it, because a new reader can read old records: it fills in the default. So the producer deploys first and stops writing amount.
Old consumers still expect amount, and their schema has no default for it. They cannot read the new records, and they fail.
BACKWARD only promised that new readers can read old records. It said nothing about old readers and new records, which is what broke.
The safe way to rename amount to total has four steps:
Add total with a default. Keep amount. The producer writes both.
Move each of the 12 consumers to total, one team at a time.
Check that no consumer reads amount any more.
Remove amount in a new version.
Remember
Set the rule to match who deploys first. If producers often deploy first, use FULL, so the registry refuses a change old consumers cannot read. And register schemas from CI, not from any producer at start-up.
Each cloud has its own streaming service. The ideas are the same: a log, an order key, and a time limit. The limits and the work differ.
This table compares them. The numbers come from each vendor's documentation in October 2026, and they change, so check them again:
Service
Unit of order and scale
Keeps records
Speaks the Kafka protocol?
Apache Kafka (your own, Amazon MSK, Confluent Cloud and others)
Partition
As long as you set, by time or size. Compacted topics keep the latest value per key.
Yes
Amazon Kinesis Data Streams
Shard. One shard takes 1 MB or 1,000 records per second of writes.
24 hours by default, up to 365 days
No
Google Cloud Pub/Sub
No partitions to manage. Order only with an ordering key.
A subscription keeps messages 7 days by default, up to 31 days
No
Azure Event Hubs
Partition
1 day (Basic), 7 days (Standard), 90 days (Premium and Dedicated)
Yes, on Standard and above
Choose Kafka when you need Kafka Connect, Kafka transactions, compacted topics, or long history that many groups replay.
Choose the cloud service when you want no brokers to run, and its limits fit. Then say which limit you checked.
Two more facts help in this talk. Kafka 4.0 runs without ZooKeeper, in KRaft mode. Since Kafka 3.9, tiered storage can move old segments to object storage, so long retention costs less disk.
Interview question
Interviewers ask: "Why Kafka here and not Kinesis?" A weak answer names a feature list. A strong answer names the requirement, such as 30 days of replay for five groups, and checks it against each service.
10 · Interview
Say it in an interview
⏱ 4 min · Topic 10 of 12
Senior Kafka questions are rarely "what is a partition". They give you a problem and ask what you would set. This table lists common ones, the short answer, and the card in the question bank:
A topic has replication factor 3 and min.insync.replicas=2. One broker is down. Is an acks=all write accepted? What if two are down?
The ISR log end offsets are 9, 9 and 7. What is the high watermark? Which offsets can a consumer read?
Your peak in two years is 90 MB per second, and one consumer does 6 MB per second. How many partitions, with some room?
In the compaction example, which record for c1 does a new consumer see first? Why?
A loader writes with INSERT. Why must you change it before you replay six hours?
12 · Next Chapter
Next Chapter
You can now run Kafka safely: copies, keys, rebalances, compaction, Connect and replay. Kafka stores the events. Something must still process them.
⏱ 3 min · Topic 12 of 12
Next chapter
Apache Flink: State, Checkpoints & Exactly-Once
You can now run Kafka safely: copies, keys, rebalances, compaction, Connect and replay. Kafka stores the events. Something must still process them.
The next chapter looks inside Apache Flink, a stream engine many teams run on Kafka. You see how it saves state, survives a crash and writes each result once.