RabbitMQ

RabbitMQ Classic vs Quorum Queues: Which to Use and When

RabbitMQ Classic vs Quorum Queues: Which to Use and When
Scott Sternloff

By Scott Sternloff, Senior Enterprise Architect

LinkedIn · Updated

The short answer: use quorum queues by default, and keep classic queues for genuinely transient, high-churn work where losing a message on node failure is acceptable. That has been the right guidance for a while now, but the reasoning matters more than the rule — because the two exceptions and the migration cost are what actually bite teams.

This question comes up constantly in RabbitMQ assessments, usually in one of two forms: an operator who inherited a cluster full of classic queues and wants to know whether converting is worth the disruption, or a team designing something new that needs a defensible default.

The Three Queue Types, Compared on What Matters

RabbitMQ has three storage mechanisms worth knowing, and the third one is not a queue at all — which is exactly why it gets misused.

Comparison matrix of RabbitMQ classic queue, quorum queue and stream across storage, read behavior, replication, whether they survive power loss, and what each is best at

The row that decides most designs is Survives power loss. A quorum queue replicates and flushes each message to disk on a quorum of nodes before it confirms to the publisher. A classic queue persists locally but has no replication, so a node failure takes the queue with it. A stream replicates but does not fsync before confirming, which means it carries the same exposure Kafka does — fine for telemetry, wrong for payments.

The row people misread is Reads. Classic and quorum queues are destructive: a consumer takes the message and it is gone. Streams are non-destructive: the message stays, and many consumers can read the same history independently at their own offset. If you find yourself wanting to re-read messages from a queue, you want a stream.

Why a Quorum Queue Can Afford to fsync

The usual objection to quorum queues is that flushing to disk on every message must be slow. It is not, and the reason is a design decision worth understanding because it is also the sharpest technical difference between RabbitMQ and Kafka.

Diagram showing a publisher writing to a quorum queue leader replica which appends to a shared write-ahead log covering multiple queues, replicated to two follower replicas, contrasting amortized fsync in RabbitMQ against per-partition fsync in Kafka

Quorum queues share a write-ahead log. One flush to disk covers messages destined for many different queues, so the cost is amortized across the whole broker instead of being paid separately by each queue. That is what makes it practical to have many queues, replication with fsync, and real throughput at the same time — three properties you normally have to choose two of.

Kafka works the other way: fsync happens per partition, which makes it expensive enough that Kafka discourages it by default and relies on replication alone. That is not a flaw so much as a different bet, but it means an acknowledged Kafka message is in the page cache of several brokers rather than on disk.

There is a second, subtler benefit. Raft in a quorum queue covers delivery state, not just message content — which message is checked out to which consumer, and how many credits that consumer holds. After a leader failover the new leader resumes with that state intact, rather than redelivering a whole batch. That mechanism is what makes per-message TTL, priorities, delays, and dead-letter routing possible at all.

How quorum queues provide durability and failover

The fsync-amortization mechanic above explains why quorum queues are fast enough to use by default. It doesn't explain how they actually survive a node dying mid-stream, which is the part worth understanding before you rely on it in production.

A quorum queue is a Raft group: an odd number of members, one per node, with one member elected leader at any given time. The leader accepts all publishes and consumer deliveries; the followers replicate the leader's log and vote in elections. When the leader goes quiet, either because the node crashed or the network dropped it, the followers notice through a missed heartbeat, wait out a randomized election timeout so two followers don't call an election at the same instant, and the first one to reach a majority of votes becomes the new leader for a fresh term. No operator action, and no client-visible downtime beyond the reconnect.

What a publisher confirm actually means: RabbitMQ only sends the confirm back once the message has been written to a majority of the group's members, not just the leader. For a three-member queue, that's two out of three. The write is durable at that point even if the node holding the third replica never comes back; losing it costs you a replica, not the message.

That majority requirement is also what defines availability at each failure count. With three members: lose one, and the remaining two still form a majority, so the queue keeps accepting writes and simply operates one replica down until the missing node returns and catches up from the Raft log. Lose two, and the surviving single node cannot form a majority on its own, so the queue stops accepting writes entirely, by design, rather than risk a member with an incomplete log becoming an unopposed source of truth. That's the same logic behind pause_minority at the cluster level, applied per queue.

This is also why an even number of members is worse than it looks, not just unnecessary. A four-member queue still only tolerates one lost member before you're at two survivors out of four, which is not a majority: you've added a whole extra replica, with its Raft overhead and memory cost, for the exact same fault tolerance as three. Member count should always land on an odd number for this reason. Replica placement follows the same logic as cluster node placement: one member per node, spread across separate hosts or availability zones, so a single infrastructure failure can't take out two members of the same queue at once.

A worked example: quorum queues for payment processing

Payment processing is the workload the durability model above was built for: every message matters, ordering matters within an account, and silent duplication is worse than a slow retry. Here's how the pieces fit together for that specific case.

Topology. A quorum queue with three members, one node per availability zone, satisfies both the durability requirement and a full-zone failure without any special-case handling. This is the same three-AZ pattern covered in our HA, disaster recovery, and cluster sizing guide; a payments workload is exactly the case that justifies not cutting it down to a single AZ to save a node.

Publisher confirms, not fire-and-forget. The service that initiates a payment waits for the broker's publish confirm before it tells the caller the payment was accepted. Given the majority-write guarantee above, that confirm means the payment instruction is durable on at least two of three nodes before anyone downstream is told it happened.

Bounded retries with a dead letter. Set x-delivery-limit on the queue so a message that keeps failing consumer processing doesn't loop forever. Once it exhausts its delivery attempts, route it to a dedicated dead-letter queue rather than dropping it. A payments DLQ should be treated as an alert condition, not a bin: anything landing there needs a human or an automated reconciliation job to look at it, because it represents a payment that didn't complete cleanly on the first several tries.

Idempotent consumers keyed on payment ID. Quorum queues, like classic queues, give at-least-once delivery. Combined with the delivery limit and DLX above, a consumer can see the same payment message more than once. Design the consumer to check (or upsert against) a payment ID it has already recorded as processed before applying the transaction again, so a redelivery is a no-op rather than a duplicate charge or a duplicate ledger entry.

Single active consumer for ordered settlement. When settlement order matters within an account, for example applying debits and credits in sequence, set x-single-active-consumer on the queue so only one consumer instance processes it at a time, with automatic failover to a backup consumer if the active one dies. This trades some throughput for strict per-queue ordering, which is usually the right trade for money movement.

What to monitor. Queue depth and consumer lag on the payments queue itself, DLQ depth (it should sit near zero and any sustained growth is an incident), redelivery count per consumer (a rising trend points at a consumer bug, not broker health), and replica sync status across the three members so a lagging replica gets caught before it's the only one left. For patterns beyond this single queue, see how the same durability guarantees compose across a broader event-driven design in exchanges, queues, and bindings for microservices, and for the full picture of how AceMQ scopes a payments-grade messaging design, see resilience and payment optimization.

What You Give Up by Converting

Most guidance stops at "use quorum queues." The useful part is the two things that actually catch people out.

Memory per queue is higher. A quorum queue carries Raft state and holds more memory than a classic queue does. If your design has thousands of small, short-lived queues — a common per-session or per-request pattern — converting all of them at once can push a cluster into memory pressure that did not exist before. This is the single most common way a well-intentioned migration causes an incident.

Priority does not work the same way. Classic queues implement priority through x-max-priority. Quorum queues handle priority differently and do not provide the identical mechanism, so a queue whose behavior depends on classic priority semantics needs its design revisited rather than simply re-declared. Find these before you plan the migration, not during it.

There is also a hard ceiling worth knowing: a quorum queue is not sharded. It tops out around 80,000 messages per second with replication and fsync, and you cannot split one queue across nodes to go faster. Aggregate throughput across many queues scales fine, but a single very hot flow needs application-level sharding — publishing across N queues with a routing key that distributes load. We cover that constraint and how it compares to Kafka partitioning in RabbitMQ vs Kafka.

Choosing, and What a Conversion Actually Costs

Four questions settle it for a given queue.

Decision chart with four questions about message loss tolerance, per-message features, churn rate and replay needs, mapping each to quorum queue, classic queue or stream, with a warning that conversion is not in place

The warning on that chart is the operational reality: queue type is fixed at declaration. There is no in-place conversion. Migrating means declaring a new quorum queue, pointing publishers and consumers at it, draining the classic queue, and removing it once empty. On a live system that is a coordinated change with a drain window, not a config edit.

The sequencing that works: convert the queues carrying work you cannot afford to lose first, in small batches, watching memory as you go. Leave genuinely transient queues classic. Resist the urge to convert everything in one pass — that is precisely the change that turns a safety improvement into an outage.

If you are weighing a conversion across a large estate and want the memory and priority gotchas found before you start rather than during, that is a normal part of a RabbitMQ health check — or talk to AceMQ about scoping it.

Several of the beliefs behind these choices no longer hold — RabbitMQ myths versus reality.

Queue type is the second of seven decisions in the RabbitMQ clustering and sizing guide; the wrong choice is also pattern four of ten in the reliability guide.

FAQ

What is the difference between a classic queue and a quorum queue?

A classic queue lives on one node with per-message persistence; if that node fails, the queue and its contents are unavailable until it returns. A quorum queue is replicated across an odd number of nodes using Raft consensus and flushes each message to disk on a quorum of replicas before confirming to the publisher. Classic is local and fast to churn; quorum is replicated and safe, and is the recommended default for anything you cannot afford to lose.

Are quorum queues slower than classic queues?

Somewhat, and by less than most people expect. A single quorum queue lands around 80,000 messages per second with replication and fsync; a classic queue runs somewhat higher because it does neither. Quorum queues stay fast because they share a write-ahead log, so one fsync covers many queues and the cost is amortized rather than paid per queue.

When should I still use a classic queue?

When the work is genuinely transient and high-churn and losing a message on node failure is acceptable — cache invalidation broadcasts, ephemeral notifications, scratch work that will be regenerated anyway. Classic queues also hold less memory per queue, which matters if you run very large numbers of small, short-lived queues.

Can I convert a classic queue to a quorum queue in place?

No. Queue type is fixed at declaration, so conversion means declaring a new quorum queue, moving publishers and consumers to it, draining the classic queue, then removing it. Plan it as a migration with a drain window. Converting thousands of queues in a single pass is the migration that most often goes wrong.

Do quorum queues support message priority?

Not in the same way. Classic queues support priority via the x-max-priority argument; quorum queues handle priority differently and do not offer the identical mechanism, so a queue depending on classic priority semantics needs its design revisited rather than simply converted. That and higher memory use per queue are the two gotchas that catch teams mid-migration.

How many nodes do quorum queues need?

An odd number, with three as the practical baseline — that tolerates one node failure while keeping a majority. Five is common in larger deployments and tolerates two. Beyond five the consensus overhead grows without much added safety, so larger clusters are usually driven by something other than quorum requirements.

Are streams a replacement for quorum queues?

No — they solve a different problem. A stream is an append-only log with non-destructive reads, so consuming does not remove the message and many consumers can read the same history independently. A quorum queue is a work queue with destructive reads and per-message acknowledgement. Streams also do not fsync before confirming, so they carry a weaker durability guarantee.

Can quorum queues survive node failures?

Yes, that's their primary purpose. A quorum queue replicates each message to a majority of its members using Raft consensus before confirming the publish, so a three-member queue keeps operating after losing one node and only stops accepting writes if it loses a second, since two of three can no longer be reached. Member placement should be one per node across separate hosts or availability zones so a single infrastructure failure can't remove more than one replica at a time.

Can quorum queues handle payment-processing throughput?

A single quorum queue tops out around 80,000 messages per second with replication and fsync, well above what most payment workloads need per queue. If a specific flow needs more than that, shard across multiple quorum queues with a routing key rather than expecting one queue to scale further, since quorum queues are not internally sharded. Combined with publisher confirms, `x-delivery-limit`, dead-lettering, and idempotent consumers, throughput is rarely the limiting factor for payments; correctness under retry is.

Free Consultation

Get Expert Eyes on Your RabbitMQ Cluster

Whether you're troubleshooting a production incident, planning a migration, or want a second opinion on your architecture — our team is ready. No pitch, just answers.

Email Us