1
0
Files
spring-messaging-demo/broker-comparison/docs/02-ordering.md
Ankur Mhatre e1f8aa7402 Add the broker-comparison module
Kafka, RabbitMQ and Pulsar measured side by side on ordering, replay and
consumer scaling by driving the three client libraries directly, plus an
operational-footprint measurement of each broker's own distribution.

Nine tests, three brokers started without Docker, and every number in the
documentation regenerated by scripts/run-all.sh.
2026-09-01 23:31:51 +05:30

3.1 KiB

prev: Three models · README · next: Replay

2. Ordering

Every broker here is FIFO with one producer and one consumer. The question that matters is what survives the second consumer, because that is the first thing you add.

Kafka: ordered within a partition, and a key picks the partition

Twelve records, three keys, three partitions (docs/output/kafka-ordering.txt):

F -> partition [2]  values [2, 5, 8, 11]
A -> partition [1]  values [1, 4, 7, 10]
D -> partition [0]  values [3, 6, 9, 12]

Each key's values come back in the order they were produced. The global sequence does not: the consumer drains one partition's buffer before moving to the next, so the delivered order is 2, 5, 8, 11, 1, 4, 7, 10, 3, 6, 9, 12.

That is the whole Kafka ordering guarantee, and it is usually enough, because "in order" nearly always means "in order per customer / per account / per device" rather than globally.

A detail worth stealing. The keys in that transcript are D, A and F, not A, B and C. With three partitions, murmur2 sends A, B and C all to partition 1. Picking three obvious keys to demonstrate partitioning would have produced a transcript in which global order was accidentally preserved. Before you conclude that your keys spread evenly, compute Utils.toPositive(Utils.murmur2(key)) % partitions for the ones you actually use.

RabbitMQ: FIFO per queue, and no key at all

One consumer sees the queue in order. Two consumers do not (docs/output/rabbit-ordering.txt):

one queue, one consumer      : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
one queue, two consumers     : [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 12]

Message 1 went to the slower consumer and finished tenth. There is no key and no partition, so there is nothing to pin related messages to one consumer. Ordering across related messages in RabbitMQ means one queue and one consumer — which means no horizontal scaling of that queue — or the consistent-hash exchange, which is a plugin and effectively reintroduces partitions by hand.

Pulsar: the subscription type decides, per subscriber

Two consumers on a Shared subscription and two on Key_Shared, same topic, same messages (docs/output/pulsar-ordering.txt):

Shared      A -> [consumer-1, consumer-2]   B -> [consumer-1, consumer-2]   C -> [consumer-1, consumer-2]
Key_Shared  A -> [consumer-2]               B -> [consumer-1]               C -> [consumer-2]

Under Shared, every key was handled by both consumers, so two messages with the same key can be in flight at once and no ordering claim survives. Under Key_Shared, each key went to exactly one consumer — Kafka's guarantee, but chosen by the subscriber rather than fixed by a partition count someone picked when the topic was created, and recomputed as consumers join and leave.

That last property is the strongest single argument for Pulsar: two teams reading the same topic can make different ordering-versus-throughput trades without negotiating.

next: Replay