Tune Consumer Prefetch for Fair Throughput
Set prefetch limits per consumer, use manual acknowledgements, and size the value from processing time so one slow message does not starve the worker pool.
Task
Adjust message consumer prefetch so throughput scales without unbounded buffering or unfair distribution.
When to use this
A consumer prefetches thousands of messages into memory, then a slow message blocks the channel and the queue looks stalled while other workers sit idle.
Prerequisites
- A message broker that supports prefetch or QoS
- Manual acknowledgement support
Steps
1. Start with prefetch equal to the number of concurrent workers times a small factor.
Expected output: Each worker keeps a few messages in flight but memory stays bounded.
2. Use manual acks so a message is not acked before processing finishes.
Expected output: Crashed workers redeliver their in-flight messages.
3. Measure end-to-end latency and backlog, then adjust prefetch in small steps.
rabbitmqctl list_queues name messages_ready messages_unacknowledgedExpected output: Prefetch is set from data, not guesswork.
4. Apply a per-message processing timeout to detect stuck handlers.
Expected output: A hung message is surfaced instead of silently blocking the channel.
Troubleshooting
Problem
Prefetch too high and memory spikes
Solution
Lower prefetch to workers times a small constant and switch to manual ack with a bounded worker pool.
Problem
Fairness across consumers
Solution
Use round-robin prefetch or per-consumer limits so one busy consumer does not hoard messages.
Limitations
- Prefetch trades latency for throughput, so real-time paths want smaller values.
- It only governs client buffering; broker-side flow control still needs separate tuning.
Context
Adjust message consumer prefetch so throughput scales without unbounded buffering or unfair distribution. A consumer prefetches thousands of messages into memory, then a slow message blocks the channel and the queue looks stalled while other workers sit idle.
Steps
- Start with prefetch equal to the number of concurrent workers times a small factor. Expected: Each worker keeps a few messages in flight but memory stays bounded.
- Use manual acks so a message is not acked before processing finishes. Expected: Crashed workers redeliver their in-flight messages.
- Measure end-to-end latency and backlog, then adjust prefetch in small steps. Expected: Prefetch is set from data, not guesswork.
- Apply a per-message processing timeout to detect stuck handlers. Expected: A hung message is surfaced instead of silently blocking the channel.
channel.prefetch(workers * 2);
await channel.consume(queue, async (msg) => {
try { await handle(msg); channel.ack(msg); }
catch { channel.nack(msg, false, false); }
}, { noAck: false });
Verify
- Each worker keeps a few messages in flight but memory stays bounded.
- Crashed workers redeliver their in-flight messages.
- Prefetch is set from data, not guesswork.
- A hung message is surfaced instead of silently blocking the channel.
Limits
- Prefetch trades latency for throughput, so real-time paths want smaller values.
- It only governs client buffering; broker-side flow control still needs separate tuning.