Kafka
0-day streakReview

Your progress

Level 1

0 XP100 XP to level 2
0day streak
0best streak
0topics started
0cards reviewed
    Review cards

    Progress is stored only in this browser. No account is required.

    Layer 4 · Full breakdown · 21 min · 5 figures

    Kafka

    What an interviewer expects you to know about Apache Kafka: the partitioned log, acks and in-sync replicas, per-key ordering, consumer groups, where exactly-once stops, partition sizing, and when to pick something else.

    Apache Kafka is a distributed, replicated, append-only log. Producers append records to topics that are split into partitions, brokers keep those records for days rather than deleting them once read, and any number of consumer groups read them at their own pace by remembering a single number per partition, the offset. In an interview it is the default answer to “move lots of events between services, in order per key, with replay”, provided you can say exactly what it guarantees, what it costs and where it stops.

    Picture it first: a courier hub’s register books

    A courier hub records every parcel event (picked up, sorted, out for delivery) in 12 register books. The book is chosen by destination PIN code, so all events for one PIN code land in one book, in order. Lines are numbered and only ever added at the bottom.

    Billing, SMS alerts and analytics each keep their own bookmark in every book, so slow analytics never holds up billing, and the SMS team can move its bookmark back to redo yesterday after a bug. Within a team, each book goes to one clerk, so a 13th clerk has nothing to read. Each book is photocopied to two other branches, and pages older than a week are shredded, read or not.

    Map it back: the set of books is a topic, one book is a partition, a line number is an offset, the PIN code is the key, a team is a consumer group, a clerk is a consumer, a bookmark is a committed offset, the photocopies are replicas, and the shredding schedule is retention.

    Data model: topics, partitions, offsets

    A record has a key, a value, a timestamp and optional headers. The broker gives it an offset, its permanent position in one partition. A topic is a named stream split into partitions, each an independent, totally ordered log.

    The producer picks the partition. With a key, the default partitioner computes murmur2(key) % partitions, so a key keeps its partition while the count stays the same. Without a key, it fills a batch on one partition before moving on (the “sticky” partitioner). Consumers pull batches from the partition leader, starting at their offset.

    A topic with three partitions. The producer hashes each record’s key to choose a partition; inside one consumer group, every partition is read by exactly one consumer.Notice Offsets only mean something inside one partition, and that every event for user_42 sits in P1 in the order it was written.

    On disk, a partition is a directory of segment files named after their first offset. The broker appends to the active segment and rolls a new one at 1 GiB (segment.bytes). Retention deletes whole closed segments, so dropping old data means deleting files, not rewriting them. A production-ready topic:

    kafka-topics.sh --bootstrap-server broker1:9092 --create --topic order-events \
      --partitions 12 --replication-factor 3 \
      --config min.insync.replicas=2 --config retention.ms=604800000

    Say the numbers explicitly: by default a new topic gets one partition, replication factor 1 and min.insync.replicas of 1.

    Retention or compaction

    Reading a record does not delete it. Each topic sets a cleanup.policy:

    • delete (default) drops segments older than retention.ms (7 days) or beyond retention.bytes. Good for standalone events: clicks, GPS pings, logs.
    • compact keeps at least the latest record per key. A null value is a tombstone that deletes the key and is itself removed after delete.retention.ms (1 day). Good for current-state streams: prices, profiles, a table’s rows.
    Log compaction on a keyed topic of product prices, before and after the cleaner runs.Notice Survivors keep their original offsets, so a compacted log has gaps, and that a delete is itself a record (the tombstone) that must outlive slow readers.

    Two caveats: the active segment is never compacted, so a reader from offset 0 still sees some superseded values; and compaction is not an index. Kafka cannot answer “what is K2’s price?” without a scan, so consumers load the topic into a cache or database.

    Guarantees, precisely

    Durability: acks, the ISR and min.insync.replicas

    Each partition has one leader, which takes all writes, and followers that fetch from it like consumers. The leader tracks the in-sync replicas (ISR); a follower that stays behind for replica.lag.time.max.ms (30 s), or whose broker stops heartbeating to the controller, is dropped. A record is committed once every ISR member has it, and consumers read only up to the high watermark, so they never see a record a failover could lose.

    The producer’s acks chooses when it hears “ok”:

    • acks=0: never waits. A broker failure silently drops records.
    • acks=1: the leader has it. If the leader dies before followers copy it, an acknowledged record is lost.
    • acks=all: every current ISR member has it. The client default since Kafka 3.0, together with enable.idempotence=true (KIP-679).

    The trap: “all” means all replicas currently in the ISR, which can shrink to the leader alone. min.insync.replicas closes the hole: with acks=all, the leader rejects writes (NotEnoughReplicas) when the ISR is smaller than this. The standard setting is replication factor 3, min.insync.replicas=2, acks=all.

    Replication factor 3 with min.insync.replicas = 2 and producers using acks = all, as brokers fall out of the in-sync set. The strip below shows the high watermark inside the leader’s log.Notice The deliberate trade: with two of three replicas gone the partition stops taking writes rather than accept data that lives on one disk.

    The documented guarantee: a committed record is not lost as long as one in-sync replica stays alive. With the standard setting you survive one broker failure with no loss and no write outage. Spread replicas across availability zones with rack awareness, or one zone outage takes two of them. Three fine points:

    • No fsync per write. “Acknowledged” means “in the page cache of every ISR member”, not “on disk”; the design notes put an fsync on every write at two to three orders of magnitude of throughput. A correlated power loss across all replicas can lose the tail.
    • Unclean leader election is off by default. If every ISR member is gone, the partition waits rather than promote a stale replica; turning it on trades data loss for availability.
    • Current 4.x releases are stricter. The high watermark no longer advances while the ISR is below min.insync.replicas, and the controller tracks Eligible Leader Replicas that are safe to promote (ELR: preview in 4.0, default for new clusters from 4.1).

    Ordering: per partition, and only per partition

    Records in one partition are delivered in the order the leader appended them. There is no order across partitions, so “ordered per user” means “keyed by user”. Three things break it:

    • Changing the partition count. % 12 and % 16 send a key to different partitions, splitting one user’s history across logs. Kafka does not move old data, and cannot reduce a partition count at all.
    • Retries without idempotence. With several requests in flight, a retried batch can land behind a later one. The idempotent producer (default) prevents this with up to 5 in flight.
    • Thread pools in the consumer, unless records are routed to workers by key.

    The cost of strict ordering is covered in message ordering and the sequential convoy.

    Delivery: at-least-once by default, exactly-once inside Kafka

    A consumer’s progress is one committed offset per partition, stored in the internal __consumer_offsets topic. When you commit sets the semantics:

    • At-most-once: commit, then process. A crash skips records.
    • At-least-once: process, then commit. A crash replays records, so writes must be idempotent. The normal choice; the 2011 paper promised only this.
    • Exactly-once (since 0.11, 2017): each record’s effect appears once, but only for Kafka → process → Kafka.

    A trap in the defaults: enable.auto.commit=true commits, every 5 seconds, whatever poll() has returned. That is at-least-once if you finish each batch before polling again, and silently at-most-once if you hand records to another thread.

    Exactly-once has two parts (Confluent, 2017):

    1. The idempotent producer. Each producer gets an ID and numbers its batches per partition; the broker drops a batch it already has. The numbers live in the replicated log, so a new leader still spots a duplicate retry.
    2. Transactions. A producer with a transactional.id writes to many partitions and commits the consumer’s offsets atomically; read_committed consumers see only committed transactions. An old instance returning from a long pause has a stale epoch and is fenced, so a “zombie” cannot commit.
    Exactly-once read-process-write. Output records and the input offsets are committed in one Kafka transaction; the call to an external system is not part of it.Notice Where exactly-once stops: anything the processor does outside Kafka can happen twice when a transaction aborts and the batch is replayed.

    The loop in Java (error handling trimmed; after an abort, rewind the consumer to its committed offsets):

    producer.initTransactions();                       // fences older instances with the same id
    while (running) {
      ConsumerRecords<String, Payment> batch = consumer.poll(Duration.ofMillis(200));
      producer.beginTransaction();
      for (var rec : batch) producer.send(toLedgerEntry(rec));
      producer.sendOffsetsToTransaction(nextOffsets(batch), consumer.groupMetadata());
      producer.commitTransaction();                    // or abortTransaction() and rewind
    }

    The cost is modest: in 2017, 1 KB records in 100 ms transactions lost about 3% of throughput against an in-order at-least-once producer. Kafka Streams wraps it all in processing.guarantee=exactly_once_v2.

    Exactly-once ends at Kafka’s edge

    The transaction covers Kafka topics and offsets, nothing else. If the processor charges a card, sends an SMS or writes to PostgreSQL, a replay repeats it. For external sinks, store the offset in the same database transaction as the result, or make the write idempotent with a key such as payment_id; see effectively-once with idempotent consumers.

    How it scales

    Partitions are the unit of everything

    A partition is the unit of write parallelism (leaders spread across brokers), read parallelism (one consumer per partition per group) and placement (its local data lives on one broker). The 2011 paper chose this on purpose: sharing a partition inside a group would need locking and per-message state. To scale, add partitions and brokers; to rebalance, move partitions.

    Why one broker is fast

    A 2014 benchmark pushed 2,024,032 records/s (100-byte records, about 193 MB/s) into three brokers with three-way replication and leader-only acknowledgement, on machines with SATA disks and 1 Gb Ethernet. The reasons:

    • Sequential I/O. Appends and in-order reads avoid seeks, so speed does not fall as data grows.
    • The page cache, not the heap. Consumers that keep up read from memory, and the cache survives a broker restart.
    • Zero-copy. sendfile moves bytes from page cache to socket without passing through the JVM. TLS disables this path.
    • Batching and compression end to end. Producers batch per partition (batch.size 16 KiB, linger.ms 5 ms since 4.0) and compress whole batches, which brokers store and serve as is.
    • No per-message bookkeeping. A consumer’s state is one integer per partition, which is why fan-out is cheap: ten groups cost ten reads, not ten copies.

    Consumer groups, lag and rebalancing

    A broker acting as group coordinator assigns partitions to a group’s consumers and reassigns them when one joins, leaves or stops heartbeating: a rebalance. Groups commit their own offsets and never block one another.

    Two consumer groups read the same four-partition topic independently. Lag is counted per partition and per group.Notice The fraud-check group is far behind on P2 while analytics on the same partition is not, and that analytics’ fifth consumer has nothing to do.

    Lag is the number to watch. The CLI shows it per partition (trimmed):

    $ kafka-consumer-groups.sh --bootstrap-server broker1:9092 --describe --group fraud-check
    TOPIC        PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG
    ride-events  0          5180            5200            20
    ride-events  2          3000            12400           9400

    Rebalances used to hurt. In the classic protocol clients compute the assignment, and its original “eager” form makes every consumer drop every partition while the group re-forms; cooperative assignment and static membership (group.instance.id) softened that. The new protocol (KIP-848) computes assignments on the broker and is fully incremental. It is GA since 4.0 and on by default on brokers, but clients opt in with group.protocol=consumer until it becomes the client default, planned for 5.0.

    The control plane: KRaft replaced ZooKeeper

    Cluster metadata (topics, leaders, ISRs, configs) used to live in ZooKeeper. KIP-500 moved it into Kafka: a quorum of controllers runs Raft over a metadata log (__cluster_metadata), and brokers fetch changes the way consumers fetch records. The timeline:

    • 3.3: KRaft production-ready for new clusters.
    • 3.5: ZooKeeper mode deprecated.
    • 3.9 (November 2024): the last release that can run with ZooKeeper; migrations happen on 3.x.
    • 4.0 (March 2025): ZooKeeper removed; KRaft only.

    Run 3 or 5 controllers; a majority must be up, so 3 tolerate one failure and 5 tolerate two.

    Tiered storage and share groups

    • Tiered storage (KIP-405, production-ready since 3.9) uploads closed segments to object storage such as S3. Brokers keep recent data locally (local.retention.ms) while retention.ms can be months, so retention no longer sets disk size and moving replicas copies less. Apache Kafka ships the interface, not an S3 plugin, and compacted topics are not supported.
    • Share groups (KIP-932, “Queues for Kafka”, production-ready in 4.2) let many consumers share one partition. Each record is locked to one consumer (30 s by default), acknowledged individually and retried up to 5 times; 4.4 added dead-letter topics. You can run more consumers than partitions, but lose ordering; delivery is at-least-once.

    Sizing partitions: the arithmetic

    Jun Rao’s rule: measure one partition’s produce rate p and consume rate c; for target throughput t, use at least max(t/p, t/c) partitions. Take a Flipkart-style sale with 50,000 order events/s at peak, about 1 KB each:

    • Producing: 50,000 × 1 KB ≈ 50 MB/s. At a measured 10 MB/s per partition, 50 / 10 = 5 partitions.
    • Consuming: each event needs a 2 ms database write, so one consumer handles 1 s / 2 ms = 500 events/s, and 50,000 / 500 = 100 partitions.
    • The consumer side wins. With headroom, about 120–150 partitions; batching the database writes would lower that.

    Storage at an average of 10,000 events/s: 10 MB/s × 86,400 s ≈ 864 GB/day; × 3 replicas ≈ 2.6 TB/day; × 7 days ≈ 18 TB across the cluster, most of which tiered storage can move off broker disks.

    Partition count is a one-way door

    You can add partitions but never remove them, and adding them remaps keys. Size for the consumer side with headroom, but not wildly: 2015 guidance capped brokers at roughly 2,000–4,000 partitions because a crashed broker’s leaders must all be re-elected. KRaft raises the ceiling, but each partition still costs files, memory and elections.

    Limits and numbers

    Defaults are from the Kafka 4.4 configuration reference (October 2026). Benchmark figures are from a stated year and hardware: quote them as orders of magnitude.

    What Number Notes
    Largest record batch 1,048,588 bytes, about 1 MB (message.max.bytes) Raise per topic, or put big payloads in object storage and send a pointer.
    Retention 7 days (retention.ms) -1 keeps data forever.
    Segment size 1 GiB Deletion and compaction work on closed segments.
    Tombstone retention 1 day (delete.retention.ms) Readers lagging longer can miss deletes.
    Producer batching batch.size 16 KiB, linger.ms 5 ms linger.ms rose from 0 to 5 in 4.0.
    Producer safety acks=all, idempotence on, ≤ 5 requests in flight Defaults since 3.0.
    New topic 1 partition, replication factor 1, min.insync.replicas 1 Always override.
    Follower dropped from ISR 30 s behind replica.lag.time.max.ms
    Consumer 500 records per poll; 5 min max between polls; 45 s session timeout Miss the poll deadline and you leave the group.
    Transaction timeout 60 s Open transactions hold back read_committed readers.
    KRaft controllers 3 or 5 Tolerate 1 or 2 failures.
    Share groups (4.2+) 30 s lock; 5 delivery attempts; 2,000 locked records per partition
    Throughput, 2014 2,024,032 records/s (100 B), 3 brokers, 3× replication, leader-only acks 421,823 records/s for one producer waiting for all in-sync replicas.
    End-to-end latency, 2014 2 ms median, 3 ms p99, 14 ms p99.9 Same benchmark.
    acks=all commit wait “on the order of 10 ms” Design documentation.
    Produce rate per partition tens of MB/s Rao, 2015.

    When to pick it, and when not

    Kafka RabbitMQ / SQS-style queue Pulsar Kinesis Data Streams DB table + outbox poller
    Core model Partitioned log, kept for days Queue; a message is deleted once acknowledged Log stored in BookKeeper, served by stateless brokers Managed partitioned log (shards) Rows in your own database
    Ordering Per partition (per key) Per queue at best; SQS FIFO per message group Per partition; Key_Shared keeps per-key order Per shard Whatever your query orders by
    Replay Any offset within retention No (RabbitMQ streams excepted) Yes Yes: 24 h default, up to 365 days Yes, if you keep the rows
    Consumer scaling Up to the partition count (share groups lift this from 4.2) Add workers freely Shared subscriptions scale past partitions Per shard; 2 MB/s read per shard Contention on one database
    Per-message retry, delay, DLQ Build it (retry topics); built in for share groups (4.4+) and Connect Built in (SQS: 30 s default visibility timeout, redrive to a DLQ) Built in (negative acks, retry and dead-letter topics) Build it Build it
    Limits to remember 1 MB default record batch SQS: 1 MiB message, 14 days max retention More moving parts: brokers, bookies, metadata store 1 MB/s or 1,000 records/s write per shard; 10 MiB record Your database’s write capacity
    Pick it for High-volume streams, fan-out, replay, CDC, stream processing Task queues and jobs with retries Many tenants and topics, geo-replication AWS-native streams without running brokers Low volume, event must commit with the DB write
    Kafka against the usual alternatives. Most rows depend on configuration; the last row is the honest reason to choose each.

    Pick Kafka when several consumers need the same events, you need per-key order at high volume, you want to replay history after a bug or to build a new view, or one change stream feeds processors, search indexes and warehouses.

    Pick something else when:

    • It is a job queue, where each task needs its own retry, delay, priority or DLQ: SQS or RabbitMQ do this out of the box (or share groups, if you already run Kafka 4.2+).
    • You need request/response. Use RPC.
    • Payloads are large. Put them in blob storage and send a reference.
    • You need lookups by key. Kafka is not a database.
    • You want a queue per user. Partitions are heavyweight; key by user on a shared topic.
    • Volume is small and the event must commit with a database write. A transactional outbox is enough, and is also how events usually get into Kafka without dual writes.

    Common interview usage

    Design problem Kafka’s role Partition key The follow-up to expect
    Distributed message queue (Kafka) You design Kafka itself — Leader election without losing committed writes; segment and index layout
    Ad click aggregation Durable buffer between the click tracker and a stream processor; replay to recompute after a bug ad_id Deduplicating clicks; exactly-once counts into the OLAP store
    Uber / food delivery Stream of location pings and order-state events feeding matching and ETA services driver_id, order_id Pings are high volume and stale in seconds: is acks=1 acceptable for them?
    Notification system Buffer between producers and per-channel senders; retry topics and a DLQ user_id Duplicate SMS after a consumer crash: idempotency keys at the sender
    Change data capture Database log → Kafka → search index, cache, warehouse; compacted topics hold the latest row Primary key Ordering per row; schema changes; rebuilding a consumer from scratch
    Payment system Ledger events published through an outbox; downstream reconciliation and notifications payment_id Exactly-once does not cover the call to the payment gateway
    Metrics and alerting / logging Ingestion buffer that absorbs bursts in front of the time-series store or log index Host or metric name Retention sizing; what happens when the index falls behind
    Top-K trending Event stream into windowed counters Item ID A viral item makes one partition hot: pre-aggregate, then merge

    In the interview

    Say what Kafka is doing in your diagram in one line, and name the key: “Ride events go to a ride-events topic with 64 partitions, keyed by ride_id, so each ride’s state changes are processed in order; replication factor 3, min.insync.replicas 2, acks=all.” That one sentence answers the first three follow-ups before they are asked.

    Going deeper: failure modes

    Hot partitions

    One key is capped at what one partition and one consumer can do. A viral product in a sale, or one giant merchant in a UPI-style payments stream, can swamp a partition, as P2 in the consumer-group figure shows. More partitions do not help. Fixes, each with a price: salt the key (merchant_42#0 … #7) and lose strict order for that merchant; pre-aggregate before Kafka or in a first stage; or give the giant its own topic.

    Slow consumers and rebalance storms

    If a batch takes longer than max.poll.interval.ms (5 minutes), the consumer is evicted and its partitions move, often to an equally slow consumer, and the group churns. Keep batches small, use static membership so restarts do not reassign, and on 4.x use the KIP-848 protocol so one slow member does not pause the rest. Alert on lag that grows steadily; see backpressure and load shedding.

    Poison pills

    A record that always fails (bad schema, a bug) blocks its partition: the consumer cannot commit past it and retries forever. Consumer groups have no built-in DLQ, so you add a retry topic with a delay, then a dead-letter topic after N attempts, plus an alert and a replay tool. Share groups (4.4+), Kafka Connect and Kafka Streams (4.2+) have this built in.

    Quiet data loss

    Kafka rarely loses data by itself; configurations do:

    • acks=1, or acks=all with min.insync.replicas=1, and a leader that dies before followers copy the tail.
    • Replication factor 1, because nobody overrode the default.
    • Unclean leader election turned on to end an outage.
    • Auto-commit with asynchronous processing.
    • Lag longer than retention. A consumer down for 8 days on a 7-day topic has no valid offset, and with auto.offset.reset=latest it silently jumps to the end. Alert on lag in time, not just records.

    Duplicates, everywhere

    Assume every consumer sees some records twice: after a crash before a commit, a rebalance, a retry with idempotence off, or a deliberate replay. Make consumers idempotent (upsert by key, a processed-IDs table, conditional writes); it is cheaper than exactly-once and covers external systems too. See delivery guarantees and DLQs.

    Kafka is not your source of truth by accident

    A topic with infinite retention can be the system of record, as in event sourcing, but then you own schema evolution for every event ever written, and nothing checks an invariant like “balance never below zero” before a write. In most interview designs the database is the source of truth and Kafka carries its changes.

    Cheat sheet

    Concept Remember it as
    Topic → partition → offset Named stream → ordered append-only log → position in that log
    Key Picks the partition (murmur2(key) % n); same key, same partition, same order
    Retention vs compaction Delete segments after 7 days (default) vs keep the latest value per key
    Consumer group Each partition goes to one member; groups are independent; offsets in __consumer_offsets
    Max useful consumers Partition count, unless you use share groups (4.2+)
    Lag Log-end offset − committed offset; alert on time behind, not just count
    Committed record On every ISR member; consumers read only up to the high watermark
    Durable setting RF 3, min.insync.replicas 2, acks=all, unclean election off, replicas across zones
    Ordering Per partition only; changing the partition count remaps keys
    Delivery At-least-once by default; exactly-once only Kafka-to-Kafka with transactions and read_committed
    Partition count max(t/p, t/c) plus headroom; can grow, never shrink
    Control plane KRaft, 3 or 5 controllers; ZooKeeper removed in 4.0 (March 2025)
    Newer features Tiered storage (3.9), KIP-848 rebalances (4.0), share groups (4.2), share-group DLQs (4.4)

    How to say it in the interview

    “I’d put a Kafka topic between the order service and everything downstream: 48 partitions keyed by order_id, so each order’s events stay in sequence while load spreads out. Replication factor 3, min.insync.replicas 2, acks=all: an acknowledged event survives a broker loss, and with two brokers down we stop taking writes rather than risk losing them. Billing, notifications and the search indexer are separate consumer groups, so a slow indexer never delays billing, and we can replay seven days after a bug. Consumers are at-least-once and idempotent; I’d use Kafka transactions only where we read and write Kafka, as in a stream processor.”

    How do you keep a user’s events in order? Key by user ID: one key maps to one partition, which one group member consumes, and the idempotent producer keeps retries in order. Don’t add partitions later, and don’t fan records out to a thread pool unless you route by key.

    Can Kafka lose an acknowledged message? With acks=all, min.insync.replicas=2 and unclean election off, only if every in-sync replica fails before flushing, say a simultaneous power loss, since Kafka does not fsync each write. With acks=1, yes: a leader can acknowledge and die before replicating.

    How many partitions would you create? max(t/p, t/c). For 50 MB/s at 10 MB/s per partition, producing needs 5; if each consumer handles 500 events/s, 50,000 events/s needs 100. Then headroom, because the count can grow but never shrink, and growing remaps keys.

    Is Kafka exactly-once? Inside Kafka, yes: the idempotent producer removes retry duplicates, and transactions commit outputs and input offsets atomically for read_committed readers. External side effects need idempotency keys or offsets stored in the same database transaction.

    What happens if one message keeps failing? It blocks its partition, because the consumer can’t commit past it. Send it to a retry topic with back-off, then a dead-letter topic after a few attempts, alert, and replay once fixed.

    Where to go next

    References and further reading

    1. Apache Kafka 4.4 documentation: Designkafka.apache.orgPersistence and page cache, batching and zero-copy, push vs pull, delivery semantics, ISR replication, unclean election, log compaction, quotas and share consumers.
    2. Apache Kafka 4.4 documentation: configuration referencekafka.apache.orgCurrent defaults quoted in the article: retention, segment size, max batch size, min.insync.replicas, acks, idempotence, linger.ms, poll and session timeouts.
    3. Apache Kafka 4.0.0 release announcement (March 2025)kafka.apache.orgFirst release without ZooKeeper; KIP-848 consumer protocol GA; Queues for Kafka in early access; Eligible Leader Replicas preview.
    4. Apache Kafka 4.2.0 release announcement (February 2026)kafka.apache.orgShare groups (Queues for Kafka) declared production-ready.
    5. KIP-500: Replace ZooKeeper with a self-managed metadata quorumcwiki.apache.orgWhy metadata moved into a Raft-replicated log inside Kafka, and how brokers fetch it.
    6. KIP-932: Queues for Kafkacwiki.apache.orgShare groups: consumers beyond the partition count, per-record acknowledgement, delivery counts, at-least-once and no ordering guarantee.
    7. Kafka: a Distributed Messaging System for Log Processing (Kreps, Narkhede, Rao, NetDB 2011)notes.stephenholiday.comThe original design: partition as the unit of parallelism, pull-based consumers, stateless brokers, at-least-once delivery.
    8. Benchmarking Apache Kafka: 2 Million Writes Per Second (On Three Cheap Machines) (Kreps, LinkedIn, 2014)engineering.linkedin.comThe 2014 hardware, throughput and end-to-end latency figures used in the limits table.
    9. Exactly-once Semantics Are Possible: Here’s How Kafka Does It (Confluent, 2017)confluent.ioIdempotent producer sequence numbers, transactions, read_committed, zombie fencing and the measured 3% throughput cost.
    10. How to choose the number of topics/partitions in a Kafka cluster? (Rao, Confluent, 2015)confluent.ioThe max(t/p, t/c) partition formula and the ZooKeeper-era per-broker partition guidance.