Acknowledgements
The promise: don’t lose work
Section titled “The promise: don’t lose work”Here’s the core question a queue must answer: if a consumer takes a message and then crashes before finishing, what happens to that work? Acknowledgements are the answer, and they’re the single most important reliability mechanism in RabbitMQ.
An acknowledgement (ack) is the consumer telling the broker: “I have fully processed this message — you can delete it now.” Until that ack arrives, RabbitMQ holds the message as unacknowledged and will redeliver it if the consumer’s channel dies.
Manual vs automatic ack
Section titled “Manual vs automatic ack”There are two modes, and the difference is enormous:
- Automatic ack (
autoAck: true/auto_ack=True/autoAck: true): the message is considered delivered the instant it leaves the broker, before your code has touched it. Fast, but if your consumer crashes mid-processing, the message is gone — RabbitMQ already forgot it. This is at-most-once, and it silently loses work. - Manual ack: RabbitMQ delivers the message but keeps it as unacknowledged until you explicitly ack. If the channel closes without an ack, the message is redelivered to another consumer. This is at-least-once, and it’s what you want for any work that matters.
The rule: use manual ack for anything you can’t afford to lose.
ack, nack, reject
Section titled “ack, nack, reject”With manual ack you have three responses:
- ack — done, delete it.
- nack (or reject) with
requeue: true— I couldn’t process it; put it back for another attempt. - nack/reject with
requeue: false— I couldn’t process it and retrying won’t help; drop it (or send it to a dead-letter exchange, if configured).
The redelivered flag on a delivery tells you “you’ve seen this one before” — a hint that a previous attempt failed, so you can treat it more carefully.
The lifecycle
Section titled “The lifecycle”sequenceDiagram
participant Q as Queue
participant C as Consumer
Q->>C: deliver (unacked)
alt processed successfully
C->>C: do the work
C->>Q: ack
Note over Q: message removed
else consumer crashes
C--xC: crash before ack
Note over Q: no ack received
Q->>C: redeliver (redelivered=true)
end Manual ack in code
Section titled “Manual ack in code”// noAck: false → manual acknowledgementchannel.consume('orders', async (msg) => { if (!msg) return; try { await handle(msg.content); channel.ack(msg); // done } catch (err) { channel.nack(msg, false, true); // requeue for another try }}, { noAck: false });def on_message(ch, method, properties, body): try: handle(body) ch.basic_ack(delivery_tag=method.delivery_tag) # done except Exception: ch.basic_nack(delivery_tag=method.delivery_tag, requeue=True) # retry
# auto_ack=False → manual acknowledgementchannel.basic_consume(queue="orders", on_message_callback=on_message, auto_ack=False)// fourth arg autoAck=false → manual acknowledgementmsgs, _ := ch.Consume(q.Name, "", false, false, false, false, nil)for d := range msgs { if err := handle(d.Body); err != nil { d.Nack(false, true) // requeue for another try continue } d.Ack(false) // done}