Skip to content
About
Main content

Choose a partitioning

The decision

Records are spread across N , shards or , say order events across the partitions of a . Where each record goes decides what can run in parallel, what stays in order, and which partition gets overloaded. Which strategy and key do you route by, and how many partitions?

You will end with a partitioning strategy and key, a partition count, and policies for skew, order, failed records and unroutable records.


Decide

1

Does anything downstream need related records together?

Per-customer state, per-order ordering, or a join on the same key.

Why

If nothing needs related records together, spreading them evenly is the only goal, and does it perfectly with no hot partitions.

2

Do you need range scans or sorted access on the key?

'All orders between two dates' or 'customers A to F' needs ranges; lookups by exact ID do not.

Why

Hashing spreads keys evenly but destroys their order. keeps order, so scans touch few partitions, but new keys that increase over time (timestamps, sequence IDs) all land in the last partition.


Where you can land

Round-robin or random

Use it when nothing downstream needs related records together.

Perfectly even load. What it costs: no per-key order and no per-key state.

Hash partitioning

Use it when related records must be together, and access is by exact key.

Before you build it

If membership will change, use a stable hash

hash(key) % N moves almost every key when N changes, which empties caches and shuffles state. For many nodes (hundreds or more): with . For fewer: , which is simpler and spreads better. For storage: many mapped onto fewer machines, so machines can change without re-hashing data.

Range partitioning

Use it when related records must be together and you scan ranges or need sorted access.

Before you build it

Spread sequential writes

If keys increase over time, every new write hits the last range. Use a (for example, a bucket number before the timestamp) to spread them.


Whichever you land on, settle these

1

Choose the key from the real distribution

The key is the entity downstream groups by. Check its number of distinct values and its actual frequency histogram, not the theoretical one.

2

Deal with skew

If a few keys carry much of the traffic (one seller at 35% of orders): isolate the hot keys, or split each hot key across M partitions by adding a random suffix and combine the partial results in a second step. That works only for aggregations whose order does not matter (sum, count, min, max), and it breaks per-key order for those keys. Often the real fix is a better key.

3

Test whether per-key order really matters

Full-state updates with a version number need no order: the highest version wins. Increments, appends and insert–delete pairs do. If you send changes (deltas): either keep strict order (one writer per key, an , one consumer thread per partition, no ) or switch to full-state updates. Switching is usually cheaper.

4

Decide what happens to a record that cannot be processed

Classify the failure first. Transient (a , a 503): with backoff, never dead-letter. Bad data: send to the dead-letter queue at once. A bug in your code: pause and alert. The downstream is down: stop consuming. Then set the dead-letter rate above which you stop instead of draining: a dead-letter queue with no limit moves your whole stream into it during an outage.

5

Decide where unroutable records go

Define the default destination for a record that matches no route, now rather than later.

6

Choose N for the ceiling, not today

At least peak ÷ one consumer's throughput, times 2–3 for headroom, and at least the most consumers you will ever want: N is to raise later. Prefer a number with many divisors (12, 24, 60, 120) so it splits evenly across consumer counts, and over-partition slightly. 2,000 orders a second at 400 per consumer gives 5, so 10 to 15 with headroom; wanting up to 24 consumers makes it 24.