Messagingkafkatopicpartitionoffsetconsumer group

Kafka-Style Logs: Topics, Partitions, Offsets

A topic is a set of append-only partition logs; a consumer group splits the partitions and remembers an offset per partition — which gives per-partition ordering, replay from any offset and many independent readers, and makes the partition key and consumer lag the two decisions that determine whether the system behaves.

▶ InteractiveInterview questionDebug it
Progress
What problem does this solve?

A queue that deletes on ack can serve one consumer and cannot be re-read. A partitioned log keeps every event for a retention window, lets Inventory, Analytics and a brand-new Fraud service each read the whole stream at their own offset, and spreads a million events per second across partitions while keeping one order’s events in order.

Topics, partitions, offsets

A topic is a name. Its data lives in N partitions, each an append-only file on disk. A record is written to exactly one partition, chosen by hash(key) mod N — or round-robin when there is no key — and receives an offset, its position in that partition. Offsets are per partition: orders-0 has offsets 0, 1, 2, … and so does orders-1; there is no global sequence number across the topic. Brokers replicate each partition to a few others (replication factor 3; a write is acknowledged when the in-sync replicas have it, acks=all).

Nothing is deleted when a consumer reads. Retention deletes by age or size (7 days, 1 TB) regardless of who has consumed. A consumer group is a named set of consumers; each partition is assigned to exactly one member of the group, and the group commits an offset per partition (group inventory: orders-0 → 1842). Two groups reading the same topic are independent — this is how one stream feeds many teams — and a new group starting at earliest gets the whole retained history: replay.

Nine events, three partitions: ordered within a partition, interleaved across them
Topic orders  (3 partitions, key = orderId)

Partition 0 → E1 → E4 → E7      offsets 0,1,2   group inventory @ 3   group analytics @ 1
Partition 1 → E2 → E5 → E8      offsets 0,1,2   group inventory @ 3   group analytics @ 3
Partition 2 → E3 → E6 → E9      offsets 0,1,2   group inventory @ 2   group analytics @ 0

E1 is before E4 is before E7  — guaranteed (same partition)
E1 versus E2                  — no order at all (different partitions)
lag(inventory) = 0 + 0 + 1 = 1   lag(analytics) = 2 + 0 + 3 = 5

The partition key decides what stays ordered

Ordering is guaranteed only within a partition, so the key is the ordering unit. Key by orderId and every event for one order — Created, Paid, Shipped — lands in one partition in write order; consumers see a coherent story per order, and different orders interleave, which nobody cares about. Key by userId and one user’s orders are ordered relative to each other too, but a single very active user (a marketplace reseller, a load test) becomes a hot partition that one consumer must drain alone while the others idle. No key at all spreads records perfectly and maximises throughput, and destroys ordering entirely — OrderUpdated can be read seconds before OrderCreated. That exact change, orderId → random key "to fix a hot partition", is the challenge events-out-of-order.

Changing the partition count changes hash(key) mod N, so events for one key move to a different partition from that moment: old events in partition 2, new ones in partition 5, and ordering across the boundary is lost for in-flight keys. Choose the partition count with headroom up front (partitions are cheap; 12–50 is ordinary) and treat repartitioning as a migration with a drain, not a config toggle.

Partition key tradeoffs for topic `orders`
KeyOrdering guaranteeDistributionTypical failure
orderIdAll events of one order in orderEven; orders are many and smallNone common — the usual right answer
userIdOne user’s orders in order across ordersSkewed by heavy usersHot partition; one consumer lags while others idle
region / tenantIdPer region / tenantVery skewed (one big tenant)One partition holds 60% of traffic; cannot scale it
none (round-robin)NonePerfectOrderUpdated consumed before OrderCreated

Consumer lag, rebalancing, compaction

Consumer lag — latest offset minus committed offset, summed over partitions — is the metric that tells you whether the system is keeping up, and it should be on the first dashboard. Lag that grows is a consumer slower than its producer, exactly the situation Backpressure describes; lag on one partition only is a hot key or a stuck consumer. Express it in time too (the timestamp of the oldest unconsumed record): "1.2 M records behind" is meaningless, "38 minutes behind" is an SLO breach.

When a consumer joins or leaves a group — deploy, crash, scale-out — the group rebalances: partitions are reassigned, and during the rebalance the affected partitions are not consumed. A group of 20 consumers redeploying one at a time triggers 20 rebalances; cooperative/incremental assignment and static membership reduce the pause, but rebalances remain a deploy-time source of lag spikes. A group can never use more consumers than there are partitions; the extras sit idle.

Compaction is the alternative to time-based retention: keep only the latest record per key, delete older ones. A compacted customer-profile topic is a changelog whose replay produces the current state of every customer, the same idea as a Hash Map rebuilt from its update history, and the basis for the read models in CQRS. Commit offsets after the effect is durable, and make the effect idempotent: at-least-once with an idempotent consumer is the honest form of exactly-once, here as everywhere (Event-Driven Architecture).

One group, three partitions, three consumers; a second group reads independently
hash(orderId) mod 3 = 0= 1= 2offset 1842offset 1790offset 1901own offsetsOrder Serviceorders-0orders-1orders-2inventory #1inventory #2analytics (1 consumer)inventory #3
ClientGateway / LBServiceWorkerDatabaseCacheQueue / LogObject storageCDNExternal system

Key points

  • A topic is N append-only partitions; an offset is a position in one partition, never a global sequence.
  • Ordering holds only within a partition; the partition key is therefore the ordering unit — orderId for orders.
  • Reads delete nothing: retention deletes by age/size, so many groups can read the same stream and replay from any offset.
  • Consumer lag (in records and in minutes) is the health metric; a group cannot exceed one consumer per partition.
  • Commit offsets after the effect is durable and keep consumers idempotent; rebalances and redeliveries are normal.

Topics, partitions, offsets, consumer groups

Topics, partitions, offsets, consumer groups
Topic orders with 3 partitions. Pick a partition key, size the consumer group, tick the clock, and read the lag.
Partition key
C0: P0 P2C1: P1
P0
E1E4E7E10
offset 4 · lag 0
P1
E2E5E8E11
offset 4 · lag 0
P2
E3E6E9E12
offset 4 · lag 0
total lag
0
max partition
4 events
idle consumers
0
replay
off
The exact guarantee: ordering holds only within a partition, and a partition is consumed by at most one consumer in a group. Every event for one order lands on the same partition, so its Created → Paid → Shipped sequence is consumed in order. Load is balanced because order ids spread evenly.
4/8 · tick 3

How data moves through it

One request or event, hop by hop.

  1. 1Order Service → Broker: produce OrderCreated with key orderId, acks=all; the broker appends to partition hash(key) mod 3 and returns the offset.
  2. 2Broker → Replicas: the partition leader replicates to two followers before acknowledging.
  3. 3Broker → inventory #2: the consumer assigned orders-1 polls a batch starting at its committed offset 1790.
  4. 4inventory #2 → Inventory DB: apply the batch idempotently (event id in processed_events), then commit offset 1802.
  5. 5Broker → analytics: the second group reads the same partitions at its own offsets; lag = latest − committed, reported per partition.
  6. 6Retention job → disk: segments older than 7 days are deleted; a compacted topic keeps the latest record per key instead.

When to use — and when not

Use it when
  • Several teams or services consume the same stream (orders → inventory, analytics, fraud, search indexing).
  • You need to replay: rebuild a projection, backfill a new consumer, or reprocess after a bug.
  • High throughput (10⁵–10⁶ events/s) with per-key ordering, e.g. per order, per account, per device.
  • Change-data-capture from a database (Replication and Read Scaling log → topic) feeding caches and read models.
Avoid it when
  • Task distribution with per-message acks, priorities and per-message retry delays — a RabbitMQ/SQS-style queue does this natively; Kafka does not.
  • Low volume, one consumer, no replay need: running a broker cluster, ZooKeeper/KRaft and lag monitoring is disproportionate.
  • Strict total ordering across all events: that is one partition, which caps throughput at one consumer.
  • Request/response: a log is not an RPC channel; see Request/Response vs Event-Driven.

Tradeoffs

Complexity
low → high
Ops cost
low → high
Latency
low → high
Consistency
weak → strong
Scalability
poor → strong

Near-linear throughput with partitions, durable history and many readers; paid for with a cluster to operate, partition-count decisions that are hard to change, rebalance pauses, and ordering that only exists per key.

How it fails

  • Partition key changed from orderId to random for throughput: OrderUpdated consumed before OrderCreated, consumers write nonsense state.
  • Hot key (userId of a reseller doing 40% of orders): one partition lags by hours while the other eleven are idle and autoscaling adds useless consumers.
  • Offsets committed before processing (auto-commit on a 5 s timer): a crash loses the records between commit and completion.
  • Retention shorter than the longest outage: a consumer down for 8 days on a 7-day topic has permanently lost data.
  • Partition count increased in production: keys reshuffle and in-flight orders split across two partitions.

How it scales

  • Throughput scales with partitions; producers and consumers scale with them up to one consumer per partition per group.
  • Add partitions with headroom before you need them; re-keying live traffic is a migration.
  • Storage scales with retention × ingest rate; tiered storage or compaction bounds it.
  • Hot keys are the ceiling: salt the key (userId#shard) and accept ordering per sub-key, or route the heavy tenant to its own topic.

How it interacts with databases, queues, caches, APIs and external systems

  • Database: consumers commit the effect and the offset (or event id) together; CDC connectors turn the DB write-ahead log into topics.
  • Queue: Kafka replaces a queue for streams but is usually paired with a task queue for per-message retry/DLQ semantics.
  • Cache: a compacted topic is the canonical way to keep a cache warm and rebuildable; Redis streams offer a lighter version (Redis: Data Structures, Not a Cache).
  • APIs: services expose events on topics as their outbound contract, with a schema registry enforcing compatibility.
  • External systems: connectors sink topics to warehouses, search indexes and object storage for analytics and replay.