Skip to main content

Partitioning, Ordering & Keys

Level 6: Level 6: Asynchronous & Event-Driven Systemshard30 minpartitioningorderingkeys

Kafka orders within a partition only, chosen by hash(key) mod N, so causally related events share a key; partition count is fixed up front, and a hot key trades ordering scope for throughput.

One ordering guarantee, and it burns candidates

Kafka gives you exactly one ordering guarantee: records are totally ordered within a partition, and there is no ordering across partitions. A partition is a single append-only sequence, so offset order equals arrival order there. But a topic with 100 partitions is 100 independent sequences interleaved by wall-clock chance. If event X lands in partition 4 and event Y lands in partition 7, Kafka makes no promise about whether a consumer sees X or Y first. There is no global clock and no global sequence.

This is not a limitation to work around; it is the direct cost of parallelism. The only reason Kafka scales horizontally is that partitions are independent, so anything needing global order would need a single partition, capping you at one broker's throughput and one consumer.

Check yourself
A ledger consumer reads a 100-partition topic and applies events assuming chronological order across the whole topic. What happens under load?

The key is correctness

A producer computes the partition as hash(key) mod partition_count (default murmur2). So all records with the same key go to the same partition and are totally ordered relative to each other. Correctness reduces to one question: which events must be seen in order relative to each other? Whatever that set is, it must share a key.

Check yourself

Which pairs of events must share a message key to stay safe, and which can live under different keys?

A deposit and a withdrawal on the same bank account
created, paid, and shipped for one order
Two messages in the same chat room
Events for two different customers' orders
One chat room's messages relative to another room's
  • Bank account: key by account_id, so deposit-then-withdraw for one account is never reordered into overdraft.
  • Order lifecycle: key by order_id, so created -> paid -> shipped stays monotonic.
  • Chat: key by conversation_id, so a room's messages stay in order even though rooms interleave.

Interview nuance: the deadliest trap is assuming global ordering. Candidates say "Kafka keeps events ordered" and design a ledger that reads events across partitions expecting chronological order. It will silently apply a withdrawal before its deposit under load. The senior framing: order is per-partition only, so causally related events must be co-keyed.

A second trap: changing partition count breaks key-to-partition stability. Because the mapping is hash(key) mod N, changing N remaps most keys. New events for account_42 land in a different partition than the old ones, so its historical order splits across two partitions for the migration window. That is why partition count is effectively immutable in practice; you over-provision up front. (Partitions can be added, never removed, and even adding reshuffles the hash.)

The hot key

One account_id (a celebrity, an omnibus account, a viral post) can flood its partition while others idle. You cannot just split it, because splitting breaks the ordering you keyed for. Options, in order of preference:

Hot key "acct_42" floods partition 3:
 (a) Compound key: hash(account_id + sub_stream) -> spread across a few
     partitions, but ordering now only holds within each sub-stream.
 (b) Salting: key = account_id + (0..k) -> k partitions, then a downstream
     merge/serializer re-establishes per-account order by sequence number.
 (c) Accept it: if the hot key truly needs strict single-stream order,
     one partition is the ceiling; scale vertically and isolate it.

Every mitigation trades ordering scope for throughput. You either keep strict per-account order (one partition, capped throughput) or widen the key and downgrade to per-sub-stream order plus reassembly.

Recap: Kafka orders within a partition only, the partition is chosen by hash(key) mod N so causally related events must share a key; there is no global order; changing partition count remaps keys and breaks ordering, so partition count is fixed up front; and a hot key forces a choice between strict order (one partition, limited throughput) and salting/compound keys (more throughput, weaker ordering plus reassembly).

Check yourself
A celebrity account floods its partition, so you salt the key and its events spread across 8 partitions. What did you just trade away?

Apply

Your turn

The task this lesson builds to.

Design partitioning for a payments ledger where all events for one account must be processed in order but the system must scale horizontally; pick the key and handle a celebrity/hot-key account.

Think about

  1. Why does ordering only hold within a partition?
  2. Why does changing partition count break key->partition stability?
  3. How do you handle a hot partition without losing ordering?

Practice

Make it stick

A second problem on the same idea, so it survives past today.

Design the partitioning key for a Coinbase-style crypto matching engine feed where every order for a given trading pair (BTC-USD) must be sequenced in strict arrival order, but BTC-USD alone can be 100x the volume of a quiet pair like a new listing. Choose the key, set partition count, and handle the case where one pair's volume exceeds a single partition's ceiling.

Think about

  1. Why can you never salt the BTC-USD stream?
  2. Where is the real bottleneck: the partition or the matcher?
  3. How do you isolate the hot pairs from the long tail?