← All topics
📬 Messaging & NServiceBus

Queue & Message Handling

Delivery guarantees, message ordering, consumer scaling.

Delivery guarantees — know the three and which one message brokers realistically give you: - At-most-once: message might be lost, never duplicated (rare in practice, generally undesirable). - At-least-once: message is never lost, but might be delivered more than once (the practical default for durable queues — this is why idempotent consumers are a hard requirement, not a nice-to-have). - Exactly-once: the ideal, genuinely hard to guarantee end-to-end across a distributed system — usually what people actually mean is "at-least-once delivery + idempotent processing", which achieves the same effective outcome without needing true exactly-once transport semantics.

Ordering — a single queue with a single consumer preserves order; scale out to multiple concurrent consumers on the same queue and you generally lose strict ordering (different messages processed at different speeds) unless you specifically partition messages (e.g. all messages for a given OrderId routed to the same consumer/partition) to preserve order within that partition only.

Consumer scaling — more consumer instances on the same queue = more parallel throughput, at the ordering cost above, and only helps up to the point the queue itself/downstream (e.g. the database) can keep up.

Flashcards (3)

What's the practical difference between at-least-once and exactly-once delivery, and which do real message brokers actually provide?
tap to reveal answer
At-least-once: message is never lost but may be delivered more than once. Exactly-once is the ideal but genuinely hard to guarantee end-to-end; most systems provide at-least-once and achieve the same practical outcome via idempotent consumers, rather than true exactly-once transport.
Why does scaling out to multiple concurrent consumers on the same queue generally break strict message ordering?
tap to reveal answer
Different consumer instances process messages independently and at different speeds, so messages can complete out of the order they were enqueued in — to preserve ordering where it matters, you partition messages (e.g. by OrderId) so related messages always go to the same consumer.
If message ordering matters for a specific entity (e.g. all events about one Order), how do you preserve it while still scaling consumers?
tap to reveal answer
Partition by a key related to that entity (e.g. OrderId) so all messages for the same entity are routed to and processed by the same consumer/partition in order, while different entities' messages can still be processed in parallel across other consumers.