DEV Community

Cover image for Stop Thinking of Kafka as a Queue. It's a Log, and That Changes Everything.
turboline-ai
turboline-ai

Posted on

Stop Thinking of Kafka as a Queue. It's a Log, and That Changes Everything.

The moment something clicks with Kafka, it usually isn't after reading the documentation. It's after you stop trying to map it onto something you already know.

Most developers come to Kafka from a background with RabbitMQ, SQS, or some other traditional message broker. That prior experience is actually the obstacle. Because Kafka looks like a queue on the surface, you point producers at it, consumers read from it, messages flow through. But the underlying model is completely different, and until you internalize that difference, a lot of Kafka's behavior feels arbitrary or over-engineered.

The Core Difference: Deletion vs. Retention

In a traditional queue, a message exists to be consumed and then discarded. RabbitMQ deletes the message once a consumer acknowledges it. SQS does the same. The queue is a temporary holding area, not a record of what happened.

Kafka treats messages as log entries. When a producer writes an event, Kafka appends it to a partitioned, ordered log and keeps it there, for hours, days, or indefinitely depending on your retention configuration. Consumption doesn't trigger deletion. The log just keeps growing.

This one design decision explains almost everything that makes Kafka unusual.

Why Multiple Consumers Stop Being a Problem

In a classic queue, if two different services both need to process the same event, you have a problem. You either duplicate the queue, build fan-out logic, or accept that only one consumer wins. None of those options are clean.

In Kafka, this isn't a problem at all. Because the log persists independently of consumption, any number of independent services can read the same stream without interfering with each other. An analytics pipeline, a fraud detection service, and a downstream database sync can all consume the same topic simultaneously. They're just reading different positions in the same log.

Offsets Are Just Bookmarks

Each consumer tracks its own position in the log using an offset, a simple integer representing how far along the log it has read. Kafka doesn't manage this on the consumer's behalf in the way a queue manages acknowledgment. The consumer owns its offset.

That's what makes replay possible. If a downstream service crashes, it can restart from its last committed offset and pick up exactly where it left off. If you're onboarding a new service and want it to process all historical events, you set its offset to zero and let it run. If you deploy a bug fix and need to reprocess the last 24 hours of data, you reset the offset and replay.

from kafka import KafkaConsumer

consumer = KafkaConsumer(
    'user-events',
    bootstrap_servers='localhost:9092',
    group_id='analytics-service',
    auto_offset_reset='earliest'  # Start from the beginning of the log
)

for message in consumer:
    print(f"Offset {message.offset}: {message.value}")
Enter fullscreen mode Exit fullscreen mode

None of that flexibility exists in a traditional queue. Once the message is gone, it's gone.

Partitions and Consumer Groups Follow Naturally

Partitions often feel like an arbitrary performance knob until you see them as a natural property of a distributed log. A single log can only be written to and read from so fast. Partitions let you split the log across multiple brokers, allowing Kafka to scale throughput horizontally.

Consumer groups are the parallel concept on the read side. When multiple instances of the same service form a consumer group, Kafka assigns each instance a subset of partitions. This gives you parallel processing without duplication. Different groups, representing different services, each get their own independent read position across all partitions.

Exactly-once delivery, which sounds almost philosophical when you first hear it, is really just a set of guarantees about how offsets are committed in relation to the work being done. When you control offset commits explicitly and use Kafka's transactional API, you can ensure that processing an event and recording that you processed it happen atomically. The log model makes that tractable in a way that fire-and-forget queuing never could.

The Mental Model Is the Unlock

You don't need to memorize Kafka's configuration surface area before it starts making sense. You need the right framing. Once you see Kafka as a distributed, durable, append-only log rather than a fast message bus, the design decisions stop feeling like complexity for its own sake.

Retention isn't just a storage setting. It's what makes replay, recovery, and multi-consumer architectures possible. Offsets aren't just an implementation detail. They're what puts the consumer in control of its own position in time. Partitions aren't just a throughput dial. They're how a single logical log scales across machines.

Get the model right first. The rest of Kafka gets a lot easier after that.

Top comments (0)