Engineering Atlas › System Design Interviews

Kafka for system design interviews

13 min read · last reviewed 2026-10-17

Open in the interactive app: practice, quiz, mock interview →

In one minute

How it works

Topics, partitions and offsets

A topic is split into partitions, each an ordered, append-only sequence of records numbered by offset. Producers choose a partition by hashing the key (murmur2 in the Java client), so all events for order-123 stay in order; keyless records stick to one partition until about batch.size bytes have gone there, then move on (producer configs). Retention is by time or size (retention.ms, 7 days by default), or by key with log compaction, which keeps the newest record per key (topic configs).

Consumer groups

Consumers sharing a group.id split a topic: each partition goes to exactly one member, so 12 partitions allow at most 12 active consumers per group. Progress is the committed offset of the next record to read, stored in the internal __consumer_offsets topic. Groups read independently, so one stream can feed an indexer, a fraud model and a warehouse.

Share groups (queues for Kafka)

Since 4.2, a share group is a production-ready alternative to a consumer group: its members read the same partitions together, each record is locked to one consumer (30 s by default) and then accepted, released for redelivery or rejected on its own; after 5 delivery attempts by default it is archived as a poison record (Kafka 4.2 upgrade notes, KIP-932). That lifts the "consumers no more than partitions" cap for job-style work, at the cost of per-partition ordering. It fits independent tasks, not ordered streams.

Delivery semantics

Process then commit gives at-least-once; commit then process gives at-most-once. Exactly-once within Kafka uses idempotent producers plus transactions that atomically write outputs and commit input offsets; external systems still need idempotent sinks.

Down the stack

Layer 1The contract

For a producer with acks=all, a successful response means the record is in the leader's log and in the log of every replica currently in the in-sync replica set (ISR), and that set had at least min.insync.replicas members; otherwise the write fails with NotEnoughReplicas. Records are visible to consumers only below the high watermark, the offset every ISR member has replicated, so a consumer never reads a record that a leader failover could erase. Since 3.0 the producer defaults are acks=all and enable.idempotence=true, up from acks=1 and no idempotence (KIP-679). Idempotent producers attach a producer ID and per-partition sequence numbers so retries do not duplicate or reorder. With acks=1, a leader crash can lose acknowledged records. Produce latency with acks=all is dominated by the follower fetch round trip and batching (linger.ms, whose default rose from 0 to 5 ms in 4.0 (upgrade notes)).

Layer 2The mechanism

Log layout. Each partition is a directory such as orders-3/. The log is split into segments, each named by its base offset: 00000000000000368771.log holds record batches, .index is a sparse map from relative offset to byte position (one entry every log.index.interval.bytes, 4 KB by default), and .timeindex maps timestamps to offsets. A segment rolls at segment.bytes (1 GiB) or segment.ms (topic configs). Retention deletes whole closed segments, never individual records, and never the active one. To read offset N, the broker binary-searches segment base offsets, then the sparse index, then scans forward a few KB.

Record batches. The unit of storage and transfer is a batch, not a record. A v2 batch header carries baseOffset, batchLength, partitionLeaderEpoch, magic, a CRC, attributes (compression codec, transactional, control), lastOffsetDelta, first and max timestamps, producerId, producerEpoch and baseSequence. Records inside store offset and timestamp as varint deltas from the header. The broker stores producer-compressed batches as is and hands them to consumers unchanged.

Replication. Followers replicate by sending Fetch requests to the leader, the same API consumers use, with the offset they want next. The leader learns each follower's log end offset from those fetches and advances the high watermark to the minimum across the ISR. A follower that has not caught up within replica.lag.time.max.ms (30 s) is dropped from the ISR (broker configs). When a leader fails, the controller picks a new leader from the ISR; with unclean.leader.election.enable=false, an out-of-sync replica is never promoted, which has been the default since 0.11 (Kafka design). Leader epochs stored in leader-epoch-checkpoint tell a returning replica exactly where to truncate divergent data.

Metadata. Since KRaft, a small Raft quorum of controllers stores cluster metadata (topics, partitions, ISR changes, configs) in the __cluster_metadata log, and brokers replay it. ZooKeeper mode was removed in 4.0, so 3.9 is the last line that can run it (upgrade notes), and failover of the controller takes about as long as a Raft election.

Groups. One broker is the group coordinator for each group, chosen by hashing the group ID onto a partition of __consumer_offsets. Assignment used to be a stop-the-world rebalance; cooperative rebalancing and the server-side consumer protocol (KIP-848, generally available since 4.0, opted into with group.protocol=consumer (rebalance protocol docs)) move partitions incrementally so most consumers keep working.

Layer 3The machine

A broker uses Java NIO: an acceptor thread, num.network.threads processor threads running selectors that read requests into a shared queue, and num.io.threads handler threads that append to logs or serve fetches. Requests that must wait, such as an acks=all produce or a fetch with fetch.min.bytes, park in a purgatory until satisfied or timed out, without holding a thread.

Writes go to the OS page cache with a plain write; Kafka does not fsync per message by default (flush.messages defaults to Long.MAX_VALUE) and relies on replication for durability, letting the kernel flush in the background (Kafka design). Reads of recent data are served from the same page cache, so consumers that keep up never touch disk. For plaintext connections, the broker uses sendfile (via FileChannel.transferTo) to move bytes from page cache to socket without copying through the JVM heap; TLS breaks this because bytes must be encrypted in user space, so the broker skips sendfile when SSL is on and pays noticeable CPU. The practical limits are NIC bandwidth (a 25 Gb/s NIC is about 3 GB/s, shared between producer ingress, replication and consumer egress) and disk throughput for consumers reading old data.

Partitions are not free: each has open file handles for its segments and indexes, replication fetch state, and metadata. Guidance has been a few thousand partitions per broker (hedge: it depends on version, hardware and KRaft), and very high partition counts slow controller failover and increase end-to-end latency.

Why it's built this way

  1. Disks and SSDs are far faster at sequential I/O than random I/O, so each partition is an append-only log and reads are sequential scans.
  2. Per-message overhead dominates at high rates, so producers batch and compress, and the broker stores and serves batches without unpacking them.
  3. Copying data through the JVM costs CPU and GC, so Kafka leans on the page cache and sendfile instead of its own cache.
  4. A log with offsets makes replay trivial, so consumers own their position and many independent groups can read the same data cheaply.
  5. Ordering across a whole topic would need one sequencer, so order is only per partition, and partitions are the unit of parallelism, replication and placement.

Diagram

sequenceDiagram
    participant P as Producer (acks=all)
    participant L as Leader (broker 1)
    participant F1 as Follower (broker 2)
    participant F2 as Follower (broker 3)
    participant C as Consumer
    P->>L: Produce batch (offsets 100-149)
    L->>L: append to active segment (page cache)
    F1->>L: Fetch(offset=100)
    L-->>F1: batch 100-149
    F2->>L: Fetch(offset=100)
    L-->>F2: batch 100-149
    F1->>L: Fetch(offset=150)
    F2->>L: Fetch(offset=150)
    L->>L: all ISR at 150, HW = 150
    L-->>P: ack
    C->>L: Fetch(offset=100)
    L-->>C: records below HW (sendfile)

Worked example

A ride-hailing app publishes driver locations: 1 million drivers, one 200-byte update every 4 seconds, so 250,000 records and 50 MB/s.

With linger.ms=10 (the default is 5 since 4.0) and zstd at roughly 4 to 1 (hedge: payload-dependent), about 12.5 MB/s reaches brokers. RF 3 means 37.5 MB/s of disk writes and 25 MB/s of follower traffic, and three consumer groups read another 37.5 MB/s. 24 hours of retention is 12.5 MB/s x 86,400 s x 3, about 3.2 TB.

Matching needs per-driver order, so the key is driver_id. At about 10,000 records per second per matching consumer, 25 consumers are needed; the topic gets 48 partitions to grow without repartitioning, since changing the count remaps keys. Six brokers lead 8 partitions each, with headroom to absorb a broker loss.

With acks=all, min.insync.replicas=2 and RF 3, one broker can fail without losing writes or blocking producers. If two fail, shared partitions reject writes rather than silently lose them, the right trade here.

In the wild

Trade-offs

OptionStrengthsWeaknessesChoose when
KafkaHigh throughput, replay, ordered partitions, huge ecosystemPartition planning, operational weight, no per-message routingEvent streaming, CDC, analytics pipelines
RabbitMQFlexible routing, per-message ack, priorities and delaysLower throughput, replay only with streamsTask queues and complex routing
SQS or Pub/SubFully managed, scales without partitionsLimited ordering and replay, per-request costSimple decoupling in one cloud
PulsarSeparate storage (BookKeeper), multi-tenancy, tiered storageMore moving parts, smaller communityMany tenants or topics, geo-replication needs

Pitfalls in production

Leadership lens

Check yourself

  1. Why can a consumer group not use more active consumers than partitions?

    Show answer

    Each partition is assigned to one consumer in the group to preserve order; extra consumers sit idle.

  2. What does the high watermark protect against?

    Show answer

    Consumers reading records that are only on the leader; if that leader failed, those records could be truncated and consumers would have seen data that no longer exists.

  3. Why does Kafka not fsync every write, and when is that unsafe?

    Show answer

    Replication across brokers provides durability more cheaply than per-write disk flushes; it is unsafe when all replicas can lose power at once, such as a single-rack cluster with RF 1 or with acks=1.

  4. Why does increasing partitions on a keyed topic break ordering?

    Show answer

    The partition is hash(key) mod count, so changing the count moves keys to new partitions while their older records remain in the old ones.

Sources and further study

Primary

Systems docs

Books

Other curricula

Watch

Free resources

Open in the interactive app: practice, quiz, mock interview →