Order stream partitioning and consumer group rebalance
How the orders topic is split, how the fulfilment group shares it, and what a join or leave does to the assignment.
Assumptions
Who writes where, who reads what
The Orders API hashes order_id to choose a partition. All events for one order land on one partition, so one instance sees them in order. Each partition has exactly one reader inside the group; a fourth partition per instance is never split.
The event on the stream
The partition key is the contract. A consumer can assume every event for one order_id arrives on the same partition in publish order; it cannot assume anything about order across two orders.
Checkout accepted an order and it is ready for fulfilment.
| Field | Type | Description | |
|---|---|---|---|
| # | order_id | uuid | Partition key; all events for this order share a partition |
| customer_id | uuid | The buyer | |
| warehouse_id | string | Where the items ship from | |
| lines | array | Items, quantities, and unit prices | |
| placed_at | timestamp | When checkout accepted the order |
| Field | Type | Description | |
|---|---|---|---|
| trace_id | string | W3C trace id |
{ "order_id": "ord_8f21", "customer_id": "cus_402", "warehouse_id": "ams-1", "placed_at": "2026-09-13T09:41:00Z" }
| Error | When |
|---|---|
| DuplicateDelivery | the same order_id and offset were already processed after a rebalance replay |
Consumers must be idempotent on order_id. A rebalance can redeliver the last uncommitted batch.
Assignment at each group size
Six partitions cap the group at six useful instances. A seventh instance joins the group, gets no partition, and idles until another instance leaves.
| Partition | 2 instances | 3 instances | 4 instances | 6 instances | 7 instances |
|---|---|---|---|---|---|
| partition 0 | fulfilment-1 | fulfilment-1 | fulfilment-1 | fulfilment-1 | fulfilment-1 |
| partition 1 | fulfilment-1 | fulfilment-1 | fulfilment-4 | fulfilment-2 | fulfilment-2 |
| partition 2 | fulfilment-1 | fulfilment-2 | fulfilment-2 | fulfilment-3 | fulfilment-3 |
| partition 3 | fulfilment-2 | fulfilment-2 | fulfilment-4 | fulfilment-4 | fulfilment-4 |
| partition 4 | fulfilment-2 | fulfilment-3 | fulfilment-3 | fulfilment-5 | fulfilment-5 |
| partition 5 | fulfilment-2 | fulfilment-3 | fulfilment-3 | fulfilment-6 | fulfilment-6 |
| idle instances | 0 | 0 | 0 | 0 | 1 (fulfilment-7) |
Warn cells are the partitions that move when fulfilment-4 joins a 3-instance group. The sticky assignor keeps every other partition where it was.
Adding an instance
A join triggers a cooperative rebalance in two rounds. Only the partitions that move are revoked, so fulfilment-3 keeps consuming through the whole rebalance. The owners of the moving partitions commit their offsets before they give the partitions up. The new instance resumes from the committed offset and replays at most one batch.
Removing an instance
The group notices a leave in one of two ways. A clean shutdown sends LeaveGroup and the rebalance starts at once. A crash sends nothing. The coordinator waits for session.timeout.ms (45 s) without a heartbeat and then evicts the member. Its partitions sit unread for up to 45 s. In both cases the survivors take over the orphaned partitions from the last committed offset.
| № | From | Event | Guard | To |
|---|---|---|---|---|
| 1 | s0 | group created | — | Empty |
| 2 | Empty | first instance joins | — | PreparingRebalance |
| 3 | PreparingRebalance | all members rejoined or rebalance timeout | — | CompletingRebalance |
| 4 | CompletingRebalance | leader sends SyncGroup assignment | — | Stable |
| 5 | Stable | instance joins | — | PreparingRebalance |
| 6 | Stable | LeaveGroup (clean shutdown) | — | PreparingRebalance |
| 7 | Stable | heartbeat missed | session.timeout.ms elapsed | PreparingRebalance |
| 8 | Stable | partition count changes | — | PreparingRebalance |
| 9 | PreparingRebalance | last member leaves | — | Empty |
| 10 | Empty | offsets expire | offsets.retention.minutes elapsed | Dead |
A Stable group handles a join and a leave the same way. It moves to PreparingRebalance, the leader recomputes the assignment, and it returns to Stable. Scale the group in steps of one instance and wait for Stable between steps; two overlapping changes double the number of partitions that move.