Why LinkedIn Built Kafka as a Commit Log Instead of a Message Queue
LinkedIn built Kafka as a distributed commit log rather than a message queue. Here's why that one decision enables replay, independent consumers, and massive throughput — and what it costs.
09 Oct 2026, 21:07 UTC

If you've ever wired two services together with a traditional message queue and then watched a third team ask for "the same data, but replayed from last Tuesday," you've hit the exact wall LinkedIn hit around 2010. Their activity tracking and log aggregation pipelines were drowning: thousands of nodes producing events, and every new consumer — search indexing, analytics, recommendations — needed its own brittle integration. Their answer was Kafka, and the interesting part isn't the scale numbers. It's one specific engineering decision: model the whole thing as a distributed commit log, not a queue.
The queue model breaks when consumers multiply
Classic message queues (think JMS-style brokers) are built around consumption semantics: a message is delivered, acknowledged, and deleted. That works for work distribution, but it fails LinkedIn's actual problem. Their data wasn't tasks to be completed — it was a stream of facts (profile views, connections, ad impressions) that many independent systems wanted to read, each at its own pace, sometimes more than once.
A commit log flips the model. Producers append events to an ordered, durable log. Consumers track their own position — an offset — and read forward whenever they like. The broker doesn't know or care who has read what. That single inversion gives you three things queues struggle with:
- Independent consumers. Search indexing and the analytics warehouse can both read the same stream without competing for messages.
- Replay. A consumer that crashes or deploys a bug can rewind its offset and reprocess history.
- Decoupling. Producers never block on slow consumers, because the log itself is the buffer.
Why sequential disk I/O was the counterintuitive win
The assumption most engineers bring is "disk is slow, so a disk-based broker must be slow." Kafka's design exploits the fact that sequential disk access is dramatically faster than random access — appending to a log file is one of the cheapest things a storage system can do, and the OS page cache absorbs a huge share of read traffic for consumers that are near the tail of the log. By accepting an append-only structure, LinkedIn got durability and high throughput from commodity disks instead of needing to keep everything in memory.
The trade-off is baked in: you can't efficiently do random lookups or per-message deletion. Retention is time- or size-based, applied to whole log segments. If your use case genuinely needs "delete this one message," a commit log fights you.
Partitions: the unit of both scale and ordering
Kafka splits each topic into partitions, and each partition is an independent ordered log that lives on one broker. This is how LinkedIn got horizontal scale: producers write to many partitions in parallel, and consumers in a consumer group each take ownership of a subset of partitions.
The subtlety — and the thing that bites people in production — is that ordering is only guaranteed within a partition. If you key messages by user ID, all events for one user land in the same partition in order. If you don't key them, you get throughput but no per-entity ordering. This is a real design decision, not a default you can ignore.
A worked example: one stream, two consumers
Say you publish a profile-views topic with 12 partitions, keyed by viewer ID. Two consumer groups subscribe:
search-indexerwith 12 consumers (one per partition) updating a search index in near real time.analytics-batchwith 4 consumers (3 partitions each) writing hourly aggregates to a warehouse.
Because offsets are tracked per consumer group, the batch group can lag hours behind the indexer with zero effect on it. And if the indexer ships a bug that corrupts entries, you can reset its offsets back 24 hours and let it reprocess — the data is still in the log. With a queue, that data would already be gone.
To see offset behavior yourself, the official Java client makes it visible:
// Run in any JVM app with the kafka-clients dependency.
// Requires network access to a broker and a topic you own.
Properties props = new Properties();
props.put("bootstrap.servers", "broker1:9092");
props.put("group.id", "analytics-batch"); // change this to see a fresh view
props.put("key.deserializer", StringDeserializer.class.getName());
props.put("value.deserializer", StringDeserializer.class.getName());
props.put("auto.offset.reset", "earliest"); // replay from the start
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(List.of("profile-views"));
ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(1));
records.forEach(r ->
System.out.printf("partition=%d offset=%d key=%s%n",
r.partition(), r.offset(), r.key()));
Run it once, note the offsets, then change group.id and run again — the new group reads from the beginning while the old group's position is untouched. That is the commit-log model in one experiment. Risk to be aware of: on a busy shared topic, earliest can pull a large backlog, so test against a low-volume topic first.
The honest costs
Kafka's design buys scale and replayability, but the operational bill is real:
- Partition management. Too few partitions caps your consumer parallelism; too many adds metadata overhead and slows rebalances. Skewed keys create hot partitions no amount of brokers will fix.
- Coordination complexity. Older Kafka versions depend on ZooKeeper for cluster metadata; newer releases use the built-in KRaft protocol, but either way you're running a distributed consensus layer. Check which mode your version uses before planning an upgrade.
- Consumer misconfiguration. Committing offsets before processing finishes loses messages on crash; committing after without idempotent processing duplicates them. "Exactly once" is something you engineer, not a switch.
What to take from LinkedIn's decision
The transferable lesson isn't "use Kafka." It's that when many independent systems need the same facts, a durable, replayable log beats a fleet of point-to-point integrations. Before adopting it, verify the fit: list your consumers, ask whether any of them will ever need to replay history, and check whether per-entity ordering matters (if it does, design your keys first). If the answers point to a log, prototype the two-consumer-group pattern above against a test cluster and watch the offsets — that behavior is the entire argument in miniature.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.