Kafka — Brokers, Partitions, Replication, Consumers
A deep-dive on Brokers, Partitions, Replication, Consumers — part of a 24-topic evergreen learning series.
Why this session matters
Part of a 24-topic learning series on engineering, ML, and LLM systems. Each session is a 90-minute deep-dive on one topic — designed so anyone can pick it up cold. Every two topics are followed by a revision session with recall prompts and hands-on drills.
Part 1: Kafka Part 1 — Brokers, Topics, Partitions, Producers
Why this session matters
It builds on the rhythm of one focused topic, paced so you have time to actually absorb it rather than rush.
Agenda
- Kafka mental model — distributed append-only log per topic-partition
- Brokers, controller, ZooKeeper-vs-KRaft — the metadata story
- Topics, partitions, offsets — the unit of parallelism
- Producer semantics — acks, idempotence, batching, compression
- Where consumers and replication fit (Part 2)
Pre-read (skim before the session)
- Confluent — Apache Kafka 101 (free course)
- Kafka design doc (official)
- Designing Data-Intensive Applications, Ch. 11
- Confluent KRaft mode docs
Deep dive
1. The one-line mental model
Kafka is a distributed, durable, append-only log partitioned across brokers. Producers append; consumers read at their own pace and track their own offset. That's it. Everything else is engineering around those primitives.
2. Why it won
- Decouples producers from consumers (multiple consumers can read the same topic).
- Durable by default — messages survive consumer restarts.
- Massive throughput — millions of events/sec on a moderate cluster.
- Becomes the event backbone for microservices, ETL, ML, search indexing.
Used at LinkedIn (origin), Uber, Netflix, Microsoft, Goldman Sachs — essentially everywhere with serious data movement.
3. Cluster topology
producers brokers (1…N) consumers
--------- --------------- ---------
▼ ▼ ▲
(write) topic A: partitions 0,1,2,3,4 (read)
topic B: partitions 0,1
│
▼
controller (one elected broker;
KRaft uses Raft, replacing ZooKeeper)
Each partition is owned by one leader broker; reads and writes go through the leader. Replicas are followers that pull from the leader.
4. Topics & partitions — the unit of parallelism
- A topic is a logical stream (e.g.
orders.placed). - A topic is split into N partitions — each is an ordered, append-only log on disk.
- Within a partition, messages are strictly ordered. Across partitions, no global order.
- Partition key decides which partition a message goes to:
partition = hash(key) % N.
Key choices:
- Pick a key so that ordering matters within the key (e.g.
user_id). All events for one user land on one partition, processed in order. - Don't pick a key with bad cardinality (
country→ hot partition for the dominant country). - Partition count is fixed-ish — you can add but not remove cleanly. Start at
2× expected concurrent consumers.
5. The controller and metadata (KRaft replaces ZooKeeper)
The controller elects partition leaders, watches broker health, and maintains the cluster metadata. Up to Kafka 2.x this lived in ZooKeeper. From Kafka 3.3+ (production-ready 3.7+), KRaft mode replaces ZooKeeper with a self-hosted Raft quorum. Less operational surface, faster failover, official direction.
6. The on-disk log
Each partition is a series of segment files (.log + .index):
topic-partition-dir/
00000000000000000000.log # offsets 0–1,099,999
00000000000000000000.index
00000000000001100000.log # 1.1M onward
00000000000001100000.index
- New writes append to the active segment.
- Old segments are deleted (retention by time/size) or compacted (keep latest per key).
- The
.indexis a sparse offset → file-offset map, used to seek without scanning. - Writes are sequential disk I/O — the killer feature. Kafka relies on the page cache more than its own caches.
7. Producer side — the knobs that matter
from confluent_kafka import Producer
p = Producer({
"bootstrap.servers": "broker:9092",
"acks": "all", # see below
"enable.idempotence": True, # see below
"compression.type": "lz4", # 'snappy' or 'lz4' for prod
"linger.ms": 5, # batch window
"batch.size": 65536, # max bytes per batch
})
p.produce("orders.placed", key=order.user_id.encode(),
value=json.dumps(order).encode())
p.flush()
Key settings:
acks:0— fire and forget. Lose messages on broker crash. Almost never.1— leader acks. Lose data if leader dies before follower replicates. OK for telemetry where some loss is fine.all— leader waits for all in-sync replicas (ISR). Standard for durable data.
enable.idempotence = True— producer dedups retries by sequence number. Required for exactly-once. Always on in modern setups.- Batching:
linger.ms = 5–20,batch.size = 64 KBtypical. Bigger batches → better throughput, slightly higher latency. - Compression:
lz4for low CPU,snappyfor compatibility. ~3–5× throughput improvement, free. - Keys: always set if you need ordering or co-location.
8. Throughput numbers (real-ish)
On a moderate 3-broker cluster (16-core, 64 GB RAM, NVMe each):
- ~1–2 GB/s sustained ingest.
- 1–2 M small messages/sec per broker.
- p99 produce latency ~5–10 ms with
acks=all+ idempotence.
These aren't promises — they're what well-tuned production looks like in 2026.
9. Common producer mistakes
- No key + ordering matters — random partitioning shuffles events for the same entity.
acks=1for financial data — silent loss on leader failover.- No idempotence + retries on — duplicates.
- Sync produce in a tight loop — single-threaded throughput. Use async +
flush()periodically. - Tiny
batch.size— misses batching benefits. - Producer per request in a web service — connection storm. Use one long-lived producer.
10. What's next (the next session — Kafka Part 2)
- Replication, ISR, unclean leader election
- Consumer groups, partition assignment, rebalances
- Exactly-once semantics (idempotence + transactions)
- Schema Registry & evolution
- Production monitoring (lag, ISR shrink, throughput)
Reading material
Books:
- Kafka: The Definitive Guide, 2nd ed. — Shapira, Palino, Sivaram, Petty (the Confluent book; chs. 1–4)
- Designing Event-Driven Systems — Ben Stopford (free O'Reilly PDF from Confluent)
- Designing Data-Intensive Applications — Kleppmann (ch. 11: Stream Processing)
Papers:
- Kafka: a Distributed Messaging System for Log Processing (Kreps et al., LinkedIn, 2011) — original Kafka paper.
- The Log: What every software engineer should know — Jay Kreps (LinkedIn essay)
Official docs:
- Apache Kafka — Documentation
- Confluent — Apache Kafka 101 course (free)
- Kafka — Producer API reference
Blog posts:
- Benchmarking Apache Kafka: 2 Million Writes per Second (LinkedIn)
- Why we built Kafka — Jay Kreps
- Kafka vs Pulsar vs Kinesis — Confluent comparison
In-depth research material
- apache/kafka — github.com/apache/kafka — ~30k ★, the source; start with
core/src/main/scala/kafka/server/. - librdkafka — github.com/confluentinc/librdkafka — C client used by everything non-JVM.
- Strimzi — Kubernetes-native Kafka operator — what most cloud installs use under the hood.
- Software Engineering Daily — Kafka episodes — multiple interviews including Jay Kreps.
- Uber Engineering — uReplicator & Kafka at Uber scale — running Kafka with 100B+ msgs/day.
- Confluent — Apache Kafka 101 course — free course with code.
Videos
- Apache Kafka 101: Brokers — Confluent · 2 min — Tim Berglund's whiteboard primer on broker responsibilities.
- Apache Kafka 101: Partitioning — Confluent · 5 min — the canonical partitioning explainer; ~170k views for a reason.
- Kafka Topics, Partitions and Offsets Explained — Stephane Maarek · 7 min — Maarek is the most-watched Kafka instructor on YouTube; clean and concise.
- Kafka Crash Course — Hands-On Project — TechWorld with Nana · 1 h 8 min — end-to-end producer + consumer + broker setup; pair with this session's lab.
- Apache Kafka in 5 minutes — Stephane Maarek · 5 min — the elevator pitch; great closer once you understand the parts.
LeetCode — Design Circular Queue
- Link: https://leetcode.com/problems/design-circular-queue/
- Difficulty: Medium
- Why this problem: Fixed array with head/tail pointers; mod arithmetic.
- Time-box: 30 minutes. Look up the editorial only after.
Assignment / Deliverables
Give yourself a clean 2-hour window and complete all of these before moving on:
- Read the deep-dive above end-to-end — no skimming. Take notes in your own words.
- Solve the LeetCode problem below without help first. Only look at the hint after 15 focused minutes; only look at editorial after 30. Log your time.
- Reproduce one code snippet locally. Pick the snippet that felt least obvious and get it running in a scratch file / notebook.
- Draw the core diagram from memory. Paper, whiteboard, or tldraw — doesn't matter. If you can't, re-read section 2 and try again.
- Write a 3-line takeaway in your prep journal: what surprised you, what you still don't understand, what you'd read next.
- Skim one item from the Reading material section. Bookmark the rest for the weekend.
- Commit any code + notes to your prep repo with message
session-NN: <one-line summary>.
Stretch (optional, +30 min): explain today's topic to a rubber duck / a friend / a voice note. If you can't teach it in 5 minutes, you don't own it yet — flag it and revisit next weekend.
Post-session checklist
By the end of this session you should be able to:
- State the one-line mental model and what each word means.
- Choose a partition key for a known workload and defend it.
- Explain acks 0/1/all trade-offs.
- Turn idempotence on and explain what it actually protects.
- Tune
linger.ms+batch.size+ compression for throughput vs latency. - Solve
design-circular-queue— same shape as Kafka segments.
Generated from sessions_data.py + content_part*.py. To edit a video / leetcode / title, edit the data file and re-run write_sessions.py.
Part 2: Kafka Part 2 — Replication, ISR, Consumer Groups, Exactly-Once
Why this session matters
It builds on the rhythm of one focused topic, paced so you have time to actually absorb it rather than rush.
Agenda
- Replication — leader/follower, ISR, high watermark, acks
- Consumer groups — rebalance, sticky vs cooperative, offset management
- Delivery semantics — at-most-once, at-least-once, exactly-once
- Transactions and the idempotent producer — what they actually guarantee
- Operating Kafka — unclean leader election, min.insync.replicas, retention
Pre-read (skim before the session)
- Confluent — Kafka Internals
- Confluent — Exactly-Once Semantics Are Possible
- KIP-98 — Exactly Once Delivery and Transactional Messaging
- Cooperative Rebalancing (KIP-429)
Deep dive
1. Replication primer
Each partition has one leader and zero or more followers (replicas). Writes go to the leader; followers Fetch to copy. The set of followers caught up to the leader is the ISR (In-Sync Replicas).
producer ── write ──▶ leader ── replicate ──▶ follower1
└────────▶ follower2
└────────▶ follower3 (lagging — kicked out of ISR)
A follower stays in ISR as long as it has fetched within replica.lag.time.max.ms (default 30 s). Drop behind and you're out — and writes can succeed without you.
2. The high watermark
The high watermark (HW) is the offset up to which all ISR replicas have replicated. Consumers can only read up to the HW. Why? Because anything past the HW might be lost if the leader dies before followers catch up.
offsets: 0 1 2 3 4 5 6 7 8
leader: ■ ■ ■ ■ ■ ■ ■ ■ ■
isr1: ■ ■ ■ ■ ■ ■ ■
isr2: ■ ■ ■ ■ ■ ■ ■
HW = 7 (consumers can read 0..6)
LEO = 9 (Log End Offset on leader)
3. acks — producer durability knob
acks=0 → fire and forget. Lowest latency, can lose unbounded messages.
acks=1 → leader acks once written to its log. Can lose 1 message on leader failure.
acks=all → leader acks after all ISR replicas have written. No data loss IF
min.insync.replicas ≥ 2 and ISR ≥ min.insync.replicas.
The gotcha: acks=all alone is not durable. If your ISR shrinks to just the leader (followers all lag), acks=all is effectively acks=1. Always pair with min.insync.replicas=2 (for RF=3) so the leader rejects writes when it can't guarantee.
4. min.insync.replicas — the actual durability knob
| RF | min.insync.replicas | Tolerates | Notes |
|---|---|---|---|
| 3 | 2 | 1 broker loss | Standard for production |
| 3 | 3 | 0 broker loss | Higher latency, no resilience |
| 3 | 1 | 2 broker loss | But data can be lost on leader failure |
When ISR \< min.insync.replicas, producers get NotEnoughReplicasException and writes pause. That's the desired behaviour — pause rather than silently risk data loss.
5. Unclean leader election
If all ISR replicas die, what happens? Two options:
unclean.leader.election.enable=false(default since 2.x): partition unavailable until an ISR replica returns. Data preserved.unclean.leader.election.enable=true: an out-of-sync replica becomes leader. Service restored; data loss (everything past the lagger's offset is gone).
Don't enable unclean election unless you're 100% sure your data can be re-derived.
6. Consumer groups
Multiple consumers in the same group share a topic — each partition assigned to exactly one consumer. Add consumers → throughput scales linearly up to # partitions. Beyond that, extra consumers sit idle.
topic: 6 partitions
group: 3 consumers
→ each consumer gets 2 partitions
group: 6 consumers
→ each consumer gets 1 partition
group: 9 consumers
→ 6 active, 3 idle
7. Rebalance — the operational landmine
When a consumer joins or leaves, Kafka rebalances: all consumers stop, partitions are reassigned. Two modes:
- Eager (legacy): everyone gives up all partitions, then re-acquires. Full stop-the-world.
- Cooperative (KIP-429, default since 2.4): only the moving partitions pause. Most consumers keep processing.
Pre-cooperative, a rolling deploy could cause 30+ seconds of cumulative pause. Cooperative cut that to seconds. Always use cooperative now (partition.assignment.strategy=org.apache.kafka.clients.consumer.CooperativeStickyAssignor).
8. Offsets — where you are in the log
Consumers commit their offset to __consumer_offsets (an internal Kafka topic). Two modes:
- Auto-commit (
enable.auto.commit=true, every 5 s): convenient, but commits before you've processed the messages → at-least-once where you might skip on crash. Avoid for non-trivial work. - Manual commit (
consumer.commitSync()after processing): commit after you've durably written / acked downstream. Standard for production.
9. Delivery semantics
| Mode | Producer | Consumer | Use case |
|---|---|---|---|
| At-most-once | acks=0 | auto-commit before process | Metrics, logs (loss OK) |
| At-least-once | acks=all | commit after process | Most pipelines (must be idempotent) |
| Exactly-once | idempotent producer + transactions | read-process-write in transaction | Financial, audit |
10. Idempotent producer (KIP-98)
Setting enable.idempotence=true makes the producer attach a producer ID (PID) and sequence number to each record. The broker dedupes any retried record with the same (PID, seq). This eliminates the classic "retry caused duplicate" bug.
Default since 3.0. Always on. Free correctness improvement.
11. Transactions
producer = KafkaProducer(transactional_id="payments-tx-1", enable_idempotence=True)
producer.init_transactions()
producer.begin_transaction()
producer.send("orders", order_msg)
producer.send("payments", payment_msg)
producer.send_offsets_to_transaction(consumer.position(), consumer_group)
producer.commit_transaction() # or abort_transaction() on error
Transactions provide atomic multi-partition writes and read-process-write exactly-once when the consumer reads in read_committed mode (skips aborted records). They cost 5–20% throughput. Worth it for billing, ledgers, audit pipelines.
12. Retention
log.retention.hours=168 # 7 days
log.retention.bytes=-1 # unlimited
log.segment.bytes=1073741824 # 1 GB segments
log.cleanup.policy=delete # or 'compact' for keyed topics
Compacted topics keep only the latest value per key (key=customer_id, value=current profile) — a Kafka topic doubles as a changelog / KV store. State stores in Kafka Streams are built on compacted topics.
13. Operating in production
- Monitor: under-replicated partitions, ISR shrinks, consumer lag, request latency.
- Capacity: aim for <70% disk usage; partitions sized so retention fits.
- Partition count: 100–1000 per broker; too many = high controller overhead, slow leader election.
- JVM tuning: G1GC, ~25% heap, rest for OS page cache (Kafka leans on page cache hard).
- Network: 10 Gbps minimum for serious throughput; producers are usually network-bound.
Reading material
Books:
- Kafka: The Definitive Guide, 2nd ed. — Shapira, Palino et al. (chs. 6–8: reliable delivery, exactly-once, replication)
- Designing Data-Intensive Applications — Kleppmann (ch. 11.4 on exactly-once semantics)
- Mastering Kafka Streams and ksqlDB — Mitch Seymour (consumer-group + EOS patterns)
Papers:
- Exactly-Once Semantics in Apache Kafka (Wang & Shapira, Confluent, 2017) — the canonical EOS writeup.
- Kafka: a Distributed Messaging System (Kreps et al., 2011) — re-read sections 4–5 on replication.
Official docs:
- Kafka — Replication design
- Kafka — Producer idempotence & transactions
- Kafka — Consumer groups & rebalance
- Confluent — KIP-98: Exactly Once Delivery and Transactional Messaging
Blog posts:
- Hardening Kafka Replication — Cloudflare
- How LinkedIn customizes Kafka for large-scale deployment
- Why Kafka Streams uses the changelog topic for state — Confluent
In-depth research material
- apache/kafka — github.com/apache/kafka — read
core/src/main/scala/kafka/cluster/Partition.scalafor ISR mechanics. - Strimzi — github.com/strimzi/strimzi-kafka-operator — Kubernetes operator with reliability defaults.
- Pinterest Engineering — Kafka & MemQ for trillions of messages
- Confluent — KIP catalogue (architecture proposals) — the engineering history.
- Software Engineering Radio — episode on Kafka with Neha Narkhede
- Uber Engineering — Kafka at Uber scale — replication tuning lessons.
Videos
- Replication in Apache Kafka® Explained — Confluent · 15 min — the official deep dive on ISR + monitoring.
- Apache Kafka — Partition Replication and ISR — Kafka Skewer · 16 min — in-sync replicas explained from first principles.
- Kafka Consumer and Consumer Groups Explained — Stephane Maarek · 5 min — the consumer-group rebalance dance, concise.
- Introduction to Consumer Group IDs — Confluent · 3 min — clarifies the most-confused config in Kafka.
- Kafka Replication Factor & ISR Explained — Prevent Data Loss — Logic Sculptor · 4 min — practical guidance on
replication.factorandmin.insync.replicassettings.
LeetCode — Design Hit Counter
- Link: https://leetcode.com/problems/design-hit-counter/
- Difficulty: Medium
- Why this problem: Bucketed queue by second; sum last 300 buckets for 5-min window.
- Time-box: 30 minutes. Look up the editorial only after.
Assignment / Deliverables
Give yourself a clean 2-hour window and complete all of these before moving on:
- Read the deep-dive above end-to-end — no skimming. Take notes in your own words.
- Solve the LeetCode problem below without help first. Only look at the hint after 15 focused minutes; only look at editorial after 30. Log your time.
- Reproduce one code snippet locally. Pick the snippet that felt least obvious and get it running in a scratch file / notebook.
- Draw the core diagram from memory. Paper, whiteboard, or tldraw — doesn't matter. If you can't, re-read section 2 and try again.
- Write a 3-line takeaway in your prep journal: what surprised you, what you still don't understand, what you'd read next.
- Skim one item from the Reading material section. Bookmark the rest for the weekend.
- Commit any code + notes to your prep repo with message
session-NN: <one-line summary>.
Stretch (optional, +30 min): explain today's topic to a rubber duck / a friend / a voice note. If you can't teach it in 5 minutes, you don't own it yet — flag it and revisit next weekend.
Post-session checklist
By the end of this session you should be able to:
- Explain the ISR + high-watermark mechanism with a diagram.
- State the difference between
acks=allandacks=all + min.insync.replicas=2. - Describe cooperative rebalancing and why it beats eager.
- Implement at-least-once consumer logic with manual offset commits.
- Explain what transactions guarantee (and what they don't).
- Solve
design-hit-counter— bucketed time window, same primitive as Kafka's log segment trimming.
Generated from sessions_data.py + content_part*.py. To edit a video / leetcode / title, edit the data file and re-run write_sessions.py.