RabbitMQ prefetch count: unacked hoarding versus idle consumers
The basic.qos prefetch count is the most consequential consumer-side setting in RabbitMQ, and the one most teams never touch. The default is unlimited. That default is why a single slow consumer can pin thousands of messages in RAM while every other consumer on the same queue sits idle, and why “we added more consumers but nothing got faster” is such a common report.
Prefetch has two opposite failure modes, diagnosed with different signals. Set too high, one consumer hoards unacknowledged messages that are pinned in broker memory and invisible to other consumers. Set too low, consumers spend most of their time waiting for the next delivery instead of processing. This guide covers the mechanism, both failure modes, and how to pick a defensible number.
What prefetch actually controls
Prefetch is a per-consumer credit limit. When a consumer subscribes with basic.consume, the broker pushes up to prefetch_count messages to that consumer without waiting for acknowledgements. Every ack frees one slot, and the broker immediately delivers one more message to fill it.
Three properties of this mechanism drive everything else:
- Unacked messages are pinned in memory. The broker must hold every unacked message available for instant redelivery if the consumer dies. Unlike ready messages, unacked messages cannot be paged to disk. Memory usage grows linearly with
unacked count x message size, a direct precursor to the memory alarm, which blocks all publishers cluster-wide. - A hoarded message is a stalled message. While a message sits in one consumer’s prefetch buffer, no other consumer can process it. If that consumer is slow, stuck, or deadlocked, every message it holds is frozen until it acks, nacks, times out, or its channel closes.
- Channel close triggers a mass requeue. When a consumer’s channel or connection dies, all of its unacked messages return to the queue in one burst. A consumer holding 10,000 unacked messages creates a 10,000-message redelivery spike when it disconnects.
Prefetch has no effect on consumers using auto-ack mode (there is nothing to wait for) or on basic.get pull consumers (each get is an explicit request). If your workload uses either, this knob does not apply.
The two failure modes
flowchart TD A[Prefetch count] --> B[Too high or unlimited] A --> C[Too low] B --> D[One consumer hoards thousands of unacked messages] D --> E[RAM pinned, other consumers idle] E --> F["Tell: unacked / (ready + unacked) > 0.8"] C --> G[Consumer acks, then waits for next delivery] G --> H[Throughput capped by ack round-trip] H --> I["Tell: consumer_utilisation < 1.0 with backlog"]
Failure mode 1: unacked hoarding
With unlimited or very high prefetch, the broker delivers messages to whichever consumer has capacity as fast as TCP allows. The first consumer to connect, or the one on the fastest network path, drains the queue into its own buffer. The queue looks almost empty in the management UI, but the work is not done: it is sitting, unacknowledged, inside one consumer.
The tell is the ratio messages_unacknowledged / (messages_ready + messages_unacknowledged). Above roughly 0.8, the vast majority of the queue’s work is in flight rather than awaiting delivery. Combined with consumer_utilisation below 1.0 and a low ack rate relative to the deliver rate, you have a hoarding consumer.
A second sanity check: unacked count on a queue should not exceed prefetch_count x active consumer count. If it does, you likely have a dead consumer whose channel never closed cleanly.
The failure escalates in three ways:
- Memory pressure. Unacked messages hold RAM on the broker. Sustained hoarding is one of the most common precursors to the memory alarm, at which point every publisher in the cluster is blocked.
- Head-of-line blocking. If the hoarding consumer hits a poison message or a slow downstream dependency, the thousands of messages it holds are stuck behind that one problem, even though other consumers are idle and healthy.
- Consumer timeout. RabbitMQ’s delivery acknowledgement timeout (
consumer_timeout, default 30 minutes) closes the channel of a consumer that holds a delivery too long. A consumer that hoards a large batch and processes it sequentially will eventually trip this, mass-requeueing everything it held. See RabbitMQ consumer timeout: delivery acknowledgement timed out and the channel is closed.
Failure mode 2: idle consumers
With prefetch set very low, especially to 1, the consumer processes a message, sends the ack, and then waits. The broker only sends the next message after the ack arrives, so every message costs at least one network round trip of dead time.
The arithmetic is brutal on high-latency paths. If the round trip between broker and consumer is 125 ms and processing takes 5 ms, a prefetch of 1 means the consumer is busy 5 ms out of every 130 ms: idle about 96% of the time. Adding consumers does not fully compensate, because each one has the same serialization.
The tell here is consumer_utilisation persistently below 1.0 on a queue that has a backlog (messages_ready > 0), combined with a deliver rate well below what the queue could sustain. The queue has work, consumers are attached, and throughput is capped by the ack round trip. See RabbitMQ consumer_utilisation low: consumers attached but not keeping up for the broader diagnostic.
consumer_utilisation on an empty queue is not meaningful. A queue with nothing to deliver can show any utilisation value; only interpret it alongside backlog.
Sizing prefetch: the working method
There is no universal number, but there is a bounded method. You are balancing two constraints.
Throughput floor: cover one round trip. To keep a consumer busy continuously, its prefetch buffer must hold at least as many messages as it can process during one broker-to-consumer round trip plus ack time:
prefetch_min ~= (round_trip_time + processing_time) / processing_time
With a 125 ms round trip and 5 ms processing, that is about 26. With a 1 ms round trip on a local network and 50 ms processing, prefetch of 2 or 3 saturates the consumer. Consumers in the same datacenter as the broker need small values; consumers across regions or the public internet need larger ones.
Memory ceiling: cap by message size. The worst-case memory a single consumer can pin on the broker is prefetch_count x max message size. With 1 MB messages and prefetch 10,000, that is 10 GB held hostage by one consumer. Divide your acceptable per-consumer memory budget by your maximum message size to get a hard ceiling:
prefetch_max ~= memory_budget_per_consumer / max_message_size
Pick a value between the floor and the ceiling. Practical notes:
- RabbitMQ maintainers have stated that prefetch values above a few hundred make virtually no difference to consumer throughput. Past that point you are buying risk, not speed.
- Prefetch of 1 is correct when fairness matters more than throughput. If messages have highly variable processing times and you want strict work distribution, prefetch 1 (or a small single-digit value) is the right choice. Accept the throughput cost as the price of even load.
- If processing time varies wildly within one queue, a large prefetch concentrates the slow tail on whichever consumer grabbed it. Prefer smaller prefetch and more consumers over large prefetch and few consumers in that case.
- Measure the actual round trip. Do not guess from region names; cross-AZ traffic inside one cloud region can already be meaningful relative to fast processing.
Enforcing a sane default
Because the broker default is unlimited, every client that forgets to set prefetch gets the dangerous behavior. RabbitMQ offers a broker-side default via the default_consumer_prefetch setting, configured in advanced.config as {rabbit, [{default_consumer_prefetch, {false, 250}}]}. This applies a per-consumer limit of 250 to consumers that do not set their own; clients that set prefetch explicitly still override it. It is a useful safety net on shared clusters where you do not control every client library configuration.
Version and queue-type differences that change the answer
- Global QoS is deprecated. The
globalflag onbasic.qosapplies one shared prefetch limit across all consumers on a channel. It was deprecated in RabbitMQ 4.0, and in a later release it moved todenied_by_default, meaningbasic.qoswithglobal=trueis rejected unless the deprecated feature is explicitly re-enabled. Client libraries that call global QoS during connection autorecovery can fail to recover after a disconnect. Use per-consumer QoS (global=false) exclusively. - Quorum queues cap unacked messages per consumer. Even with prefetch set to 0 (unlimited), a quorum queue will not deliver more than a bounded number of unacked messages to a single consumer. Operators migrating from classic queues who relied on unlimited prefetch for batch processing will hit this ceiling; the maintainers’ recommendation for that pattern is streams.
- Classic mirrored queues are gone. They were removed in RabbitMQ 4.0, so any prefetch behavior you observed on mirrored queues no longer applies after migration to quorum queues.
- Consumer timeout gives hoarders a hard clock. The delivery acknowledgement timeout (
consumer_timeout, default 30 minutes) closes the channel of a consumer that holds a delivery too long. A large prefetch combined with slow per-message processing turns this timeout from a safety net into a recurring channel-churn event.
Signals to watch in production
| Signal | Why it matters | Warning sign |
|---|---|---|
messages_unacknowledged per queue | Direct measure of in-flight work pinned in RAM | Growing without bound; or steady at prefetch x consumers with a low ack rate |
unacked / (ready + unacked) ratio | Distinguishes hoarding from backlog | Sustained above 0.8 means nearly all work is in flight |
consumer_utilisation per queue | Whether attached consumers can accept deliveries | Below 1.0 with messages_ready > 0 means consumers are the bottleneck |
| Ack rate vs deliver rate | Confirms processing completes | Deliver high, ack near zero: consumers receive but never finish |
| Deliver rate vs publish rate | Whether the queue drains | Deliver capped well below what backlog should allow: prefetch too low |
| Redeliver rate | Mass requeues from dead or timed-out consumers | Spikes correlated with consumer disconnects or timeout channel closures |
mem_used / mem_limit | Unacked hoarding ends here | Ratio climbing in step with unacked growth |
Correlate these, do not read them in isolation. A queue with 50,000 ready messages and utilisation 1.0 is a capacity problem (add consumers). A queue with 200 ready, 40,000 unacked, and utilisation 0.3 is a hoarding problem (fix prefetch and the stuck consumer). Same total depth, opposite fixes.
How Netdata helps
- Tracks
messages_readyandmessages_unacknowledgedas separate per-queue series, so the hoarding ratio is visible at a glance instead of requiring mental arithmetic on the management UI. - Collects per-queue message rates (publish, deliver, ack, redeliver), which is what you need to distinguish “consumers stuck” from “prefetch too low” from “queue simply overloaded”.
- Correlates queue-level unacked growth with node memory usage on one dashboard, so you can see hoarding turning into memory pressure before the memory alarm fires.
- Exposes
consumer_utilisationper queue alongside depth, making the idle-consumer pattern (backlog plus low utilisation) directly observable. - Per-second granularity catches the requeue burst when a hoarding consumer’s channel closes, which slower polling intervals smooth into invisibility.
Related guides
- RabbitMQ consumer timeout: delivery acknowledgement timed out and the channel is closed
- RabbitMQ consumer_utilisation low: consumers attached but not keeping up
- RabbitMQ consumers connected but not acknowledging: the consumer black hole
- RabbitMQ head message age: the queue latency that depth alone cannot show
- RabbitMQ memory resource limit alarm: publishers blocked across the whole cluster
- RabbitMQ connection in flow state: credit-based backpressure explained
- RabbitMQ flow control vs resource alarms: the two throttling mechanisms operators confuse
- RabbitMQ monitoring checklist: the signals every production broker needs
- How RabbitMQ actually works in production: a mental model for operators






