Why Acknowledgments Exist
A message acknowledgment (ack) is the signal a consumer sends back to RabbitMQ confirming it has fully processed a message and the broker can safely delete it from the queue. Without acknowledgments, RabbitMQ would have to assume delivery equals success, which is dangerous — a consumer could crash mid-processing, losing the message forever with no way to recover it. By default, RabbitMQ consumers use manual acknowledgment mode, meaning the broker holds a message as 'unacked' from the moment it is delivered until the consumer explicitly calls basic_ack, and will redeliver it to another consumer if the original one disconnects first.
Cricket analogy: It is like a fielder only being credited with a clean catch once the umpire's soft-signal review confirms the ball didn't touch the ground, rather than the broadcast assuming a catch is clean the instant the fielder claims it.
Manual Ack, Nack, and Reject
Beyond the simple basic_ack, RabbitMQ supports basic_nack and basic_reject for cases where processing fails. basic_nack (which can negatively acknowledge one message or, with multiple=True, a whole batch) and basic_reject both let the consumer signal failure, and each accepts a requeue flag: requeue=True puts the message back at the front of the queue for redelivery, while requeue=False either discards it or, if a dead-letter exchange is configured, routes it there for inspection or retry with backoff. Choosing requeue=True carelessly on a message that always fails processing creates a poison-message loop, where the same message is redelivered and rejected forever, burning CPU without making progress.
Cricket analogy: It is like a third umpire either confirming a run-out (ack), sending it back for another camera angle (nack with requeue), or ruling it unreviewable and moving on (reject without requeue) — but repeatedly requesting the same inconclusive replay forever would just stall the match without ever reaching a decision.
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host="localhost"))
channel = connection.channel()
channel.queue_declare(queue="image_resize", durable=True)
channel.basic_qos(prefetch_count=1)
def callback(ch, method, properties, body):
try:
resize_image(body)
ch.basic_ack(delivery_tag=method.delivery_tag)
except TransientError:
# Temporary failure: retry later
ch.basic_nack(delivery_tag=method.delivery_tag, requeue=True)
except CorruptFileError:
# Permanent failure: don't requeue, route to DLX instead
ch.basic_nack(delivery_tag=method.delivery_tag, requeue=False)
channel.basic_consume(queue="image_resize", on_message_callback=callback)
channel.start_consuming()
Configure a dead-letter exchange (x-dead-letter-exchange) on the queue so that messages rejected with requeue=False land in a separate queue for inspection, alerting, or manual replay instead of vanishing silently.
Auto-Ack: Convenient but Risky
Setting auto_ack=True (or the deprecated no_ack=True) tells RabbitMQ to consider a message acknowledged the instant it is delivered over the network, before the consumer has done any work. This trades safety for a small throughput gain and the removal of prefetch-based flow control, since the broker no longer needs to track unacked messages per consumer. Auto-ack is reasonable for purely disposable, low-value telemetry where an occasional dropped message is acceptable, but it is the wrong choice for any task — order processing, payment events, job execution — where losing a message silently would cause real damage.
Cricket analogy: It is like a scorer marking a run as confirmed the instant the batters start running, before the fielder's throw and stumping attempt are even resolved — fine for a rough live text commentary feed, but unacceptable for the official scorecard that decides the match result.
Auto-ack silently discards RabbitMQ's core reliability guarantee: if the consumer process crashes after delivery but before finishing its work, the message is gone permanently with no redelivery. Never use auto-ack for anything you cannot afford to lose.
- An acknowledgment (ack) tells RabbitMQ a message was fully processed and can be safely removed from the queue.
- Manual acknowledgment mode holds a message as 'unacked' until basic_ack is called, and redelivers it if the consumer disconnects first.
- basic_nack and basic_reject signal processing failure; the requeue flag decides whether the message is retried or discarded/dead-lettered.
- Careless requeue=True on permanently-failing messages creates a poison-message loop that burns CPU without progress.
- Dead-letter exchanges (x-dead-letter-exchange) capture rejected, non-requeued messages for inspection instead of silently dropping them.
- Auto-ack trades reliability for a small throughput gain and should be reserved for genuinely disposable, low-value data.
- prefetch-based fair dispatch only works with manual acknowledgment, since it depends on knowing which messages are still unacked.
Practice what you learned
1. What does a manual acknowledgment (basic_ack) tell RabbitMQ?
2. What happens to an unacked message if its consumer's connection closes unexpectedly?
3. What risk does setting requeue=True introduce for a message that always fails processing?
4. What is the purpose of a dead-letter exchange in relation to acknowledgments?
5. Why is auto-ack considered risky for important data?
Was this page helpful?
You May Also Like
Work Queues and Load Balancing
Learn how RabbitMQ distributes tasks across a pool of competing consumers, and how prefetch settings enable fair, load-based dispatch.
Queues Explained
Understand what a RabbitMQ queue is, how it stores messages, and the properties that control its lifecycle and behavior.
RPC Pattern with RabbitMQ
Learn how to implement request/response Remote Procedure Calls over RabbitMQ using reply-to queues and correlation IDs.