Siddhant Deval
Siddhant Deval
backend18 min read

Apache Kafka Deep Dive: Commit Log, Partitions, Consumer Groups & Delivery Guarantees

Kafka's architectural advantage is not speed but immutability — the append-only, partitioned commit log provides ordered replay, durable retention, and independent consumer offset management. Master partition topology, consumer group rebalancing, and exactly-once semantics via the transactional producer API.

Apache Kafka Deep Dive: Commit Log, Partitions, Consumer Groups & Delivery Guarantees

Every boundary is a failure isolation decision — and nowhere is this more consequential than in how you publish and consume events. The most common failure we see in teams adopting event-driven architecture is treating Kafka as a faster RabbitMQ: they publish messages, consume them once, and move on. They leave enable.auto.commit=true, set a partition count of 1, and never configure acks. Then, when their consumer group restarts during high traffic, they discover that Kafka's rebalance protocol has stopped all partition processing for 45 seconds while a new leader assignment completes — and they have no idea what happened because no one taught them what the commit log model actually is.

Architectural Note

Series positioning: This is Part 3 of the Distributed Architecture & System Design series. Building on the inter-service communication paradigms from Part 2: Service-to-Service Communication, it analyzes the mechanics of Apache Kafka's append-only commit log, partition topologies, and consumer groups before exploring AMQP broker routing in Part 4: RabbitMQ Topologies.


1. The Immutable Commit Log

When a consumer reads a message from RabbitMQ, the message is acknowledged and deleted. When a consumer reads a message from Kafka, nothing changes. The message stays on disk, in order, until the configured retention period expires.

Three independent consumer groups read the same immutable log at their own pace. Adding a new consumer group never affects existing consumers.

Kafka partitioned commit log architecture showing topic partitions distributed across brokers with sequential segment files, producer hash-based routing, and independent consumer group offset pointers.
Kafka partitioned commit log architecture showing topic partitions distributed across brokers with sequential segment files, producer hash-based routing, and…
Mental Model Check

A Kafka topic is a distributed, durable, ordered file. Producing is an append. Consuming is a read with a bookmark (offset). The file is shared; the bookmarks are independent. This is why Kafka enables event sourcing, CDC, and replay-based debugging — properties that vanish the moment you treat it like a delete-on-read queue.


2. Broker Architecture & Replication

2.1 Leaders, Followers, and ISR

Each partition is stored on one leader broker and replicated to follower brokers. The In-Sync Replica (ISR) set is the subset of followers fully caught up with the leader.

2.2 Durability Configuration

PROPERTIES
# Broker / topic config
replication.factor=3
min.insync.replicas=2   # at least leader + 1 follower must confirm

# Producer config
acks=all                # wait for full ISR acknowledgement
Scenario Outcome
1 broker failure (ISR=2, min=2) Cluster operational ✅
2 broker failures NotEnoughReplicas error — producers blocked, data preserved ✅
acks=1 + 1 broker failure Unacknowledged messages in flight may be lost ❌

2.3 KRaft Mode (Kafka 3.x+)

Aspect ZooKeeper Mode KRaft Mode
Metadata storage External ZooKeeper ensemble Internal Kafka Raft log
Operational components Kafka + ZooKeeper Kafka only
Controller failover Seconds Sub-second
Max partitions (practical) ~200K ~1M+

3. Partitioning Strategy & Key Design

3.1 Partition Count = Parallelism Ceiling

Topic: order.events — 3 partitions

Consumer Group: fulfillment-service (3 consumers) — OPTIMAL
  Consumer 0 → Partition 0
  Consumer 1 → Partition 1
  Consumer 2 → Partition 2

Consumer Group: fulfillment-service (5 consumers) — WASTEFUL
  Consumer 0 → Partition 0
  Consumer 1 → Partition 1
  Consumer 2 → Partition 2
  Consumer 3 → ∅ (idle — no partition)
  Consumer 4 → ∅ (idle — no partition)
Performance / Safety Warning

You cannot scale a consumer group beyond its topic's partition count. Adding a fourth consumer to a three-partition topic leaves it permanently idle. Partition count is a lifetime capacity planning decision — increasing it later may break ordering guarantees for key-based producers.

3.2 Key-Based Ordering

TYPESCRIPT
// ✅ Key-based routing — all events for the same order land on the same partition
await producer.send({
  topic: 'order.events',
  messages: [{
    key: orderId,  // CRC32(key) % partitionCount → deterministic partition
    value: JSON.stringify({ type: 'OrderPlaced', orderId, userId }),
  }],
})

// ❌ Null key → round-robin partitioning → no ordering guarantee per entity
await producer.send({
  topic: 'system.metrics',
  messages: [{ key: null, value: JSON.stringify(metric) }], // ordering irrelevant for metrics
})

4. Consumer Groups & Rebalancing

4.1 Stop-the-World Rebalance

4.2 Cooperative Incremental Rebalancing

TYPESCRIPT
// ✅ Kafka 2.4+ — only revoke partitions that actually need to move
const consumer = kafka.consumer({
  groupId: 'fulfillment-service',
  partitionAssigners: [PartitionAssigners.cooperativeStickyAssignor],
})
Rebalance Type Behavior Pause Duration
Eager (default) All consumers stop and revoke ALL partitions 5–30s (full group)
Cooperative Incremental Only consumers whose partitions change stop < 1s
Consumer group rebalancing comparison contrasting eager stop-the-world revocation of all partitions (left) against cooperative sticky incremental reassignment maintaining active throughput (right).
Consumer group rebalancing comparison contrasting eager stop-the-world revocation of all partitions (left) against cooperative sticky incremental reassignmen…

4.3 Key Consumer Configuration

PROPERTIES
session.timeout.ms=45000       # declare consumer dead after 45s without heartbeat
heartbeat.interval.ms=3000     # send heartbeat every 3s
max.poll.interval.ms=300000    # rebalance triggered if poll() not called within 5min
                               # increase for slow message processing (DB writes, API calls)

5. Delivery Guarantee Semantics

5.1 At-Most-Once — Fire and Forget

TYPESCRIPT
// ❌ At-most-once — data loss possible on crash or broker failure
await consumer.run({
  autoCommit: true,        // offset committed on poll — BEFORE processing
  eachMessage: async ({ message }) => {
    await riskyProcess(message) // if this throws: message LOST (offset already committed)
  }
})

5.2 At-Least-Once — The Production Default

TYPESCRIPT
// ✅ At-least-once — no data loss, but duplicates possible; consumer must be idempotent
await consumer.run({
  autoCommit: false,
  eachMessage: async ({ topic, partition, message }) => {
    await process(message)   // process FIRST
    await consumer.commitOffsets([{  // THEN commit offset
      topic, partition,
      offset: (Number(message.offset) + 1).toString()
    }])
    // Crash between process() and commitOffsets() → redelivery on restart → duplicate
  }
})

5.3 Exactly-Once — Transactional API

TYPESCRIPT
// ✅ Exactly-once — atomic commit of output + input offset in one transaction
const producer = kafka.producer({
  idempotent: true,
  transactionalId: 'order-processor-p0', // unique per producer instance
  acks: 'all',
})

await producer.transaction(async (txn) => {
  const results = await processIncomingBatch(batch)
  await txn.send({ topic: 'processed.orders', messages: results })
  await txn.sendOffsets({
    consumerGroupId: 'order-processor',
    topics: [{ topic: 'raw.orders', partitions: [{ partition: 0, offset: '42' }] }]
  })
  // Either both commit atomically or neither commits
})

5.4 Delivery Guarantee Comparison

Guarantee Data Loss Duplicates Producer Config Consumer Commit Overhead
At-most-once Possible Never acks=0 or acks=1 Before processing Lowest
At-least-once Never Possible acks=all, retries After processing Low
Exactly-once Never Never idempotent=true + transactionalId Inside transaction ~10–20% throughput
Crucial Requirement

Exactly-once requires both idempotent producers AND the transactional API. Idempotent producers prevent broker-side duplicates from retries. They do NOT prevent consumer-side duplicates from rebalances. End-to-end exactly-once requires atomic commit of output messages AND input consumer offsets.


6. Producer Configuration Deep Dive

PROPERTIES
# High-durability exactly-once producer configuration
acks=all
enable.idempotence=true
retries=2147483647                          # unlimited — safe with idempotence
max.in.flight.requests.per.connection=5    # safe with idempotence; without: must be 1
batch.size=16384                           # 16KB — increase for higher throughput
linger.ms=5                               # wait up to 5ms for batch to fill
compression.type=snappy
transactional.id=my-service-p0            # unique per producer instance
transaction.timeout.ms=60000

7. Consumer Lag & Dead-Letter Queues

BASH
# Monitor consumer lag per partition
kafka-consumer-groups.sh \
  --bootstrap-server kafka:9092 \
  --describe --group fulfillment-service

# TOPIC         PARTITION  LOG-END-OFFSET  CURRENT-OFFSET  LAG
# order.events  0          10450           10432           18
# order.events  1          9910            9871            39
# order.events  2          11203           11203           0    ← healthy
TYPESCRIPT
// ✅ Dead-letter queue — skip poison pills without blocking the partition
await consumer.run({
  autoCommit: false,
  eachMessage: async ({ topic, partition, message }) => {
    const retries = Number(message.headers?.['x-retry-count'] ?? 0)
    try {
      await processMessage(message)
      await commitOffset(topic, partition, message.offset)
    } catch (err) {
      if (retries >= 3) {
        await dlqProducer.send({
          topic: `${topic}.dlq`,
          messages: [{ key: message.key, value: message.value,
            headers: { 'x-original-topic': topic, 'x-error': err.message } }]
        })
        await commitOffset(topic, partition, message.offset) // skip and continue
      } else {
        throw err // retry framework increments x-retry-count
      }
    }
  }
})

Summary

Architectural Concern Production Rule
Immutable Commit Log Kafka never deletes consumed messages; consumer groups maintain independent offsets enabling replay, fan-out, and time-travel.
Partition Parallelism The hard ceiling on consumer group parallelism; set at topic creation based on throughput and ordering requirements.
At-Least-Once Delivery Requires manual offset commit AFTER processing; consumers must be idempotent to handle replays safely.
Exactly-Once Semantics Requires idempotent producers AND transactional APIs; guarantees atomic commit of output messages + input offset.
Cooperative Rebalancing Kafka 2.4+ CooperativeStickyAssignor eliminates stop-the-world pauses by revoking only moved partitions.

What's Next

Now that we have analyzed Kafka's commit log mechanics, Part 4: RabbitMQ & AMQP Messaging Topologies explores broker-mediated routing, direct/topic/fanout exchanges, quorum queues, and the decision matrix between Kafka and RabbitMQ.

Research & Synthesis Note

This article was developed with AI-assisted deep search, specification cross-referencing, and technical research synthesis.

#Kafka#Event-Driven#Distributed Systems#Messaging#Backend
Siddhant Deval

Written by Siddhant Deval

Senior Full-Stack Engineer building high-scale architectures, browser performance engineering systems, and SaaS platforms.