amqp-node / amqplib

AMQP 0-9-1 library and client for Node.JS
https://amqp-node.github.io/amqplib/
Other
3.69k stars 474 forks source link

How to determine if channel.ack() is successful? #749

Closed lunode closed 9 months ago

lunode commented 10 months ago
const amqp = require("amqplib");
(async function () {
  const conn = await amqp.connect("amqp://ali:5672");
  const ch1 = await conn.createChannel();
  ch1.on("error", console.log);
  ch1.on("close", console.log);
  ch1.on("return", console.log);
  await ch1.prefetch(1);
  ch1.consume(
    "queue",
    async (msg) => {
      console.log(msg.content.toString());
      // do a long time task
      setTimeout(() => {
        ch1.ack(msg);
      }, 10 * 1000);
      // ch1.ack(msg)
    },
    { noAck: false }
  );
})();

if the ch1.ack() failed because of the network unestablished, it didn't throw any exception duration the task execution and return nothing, how could I know the ack result and rollback the task?

cressie176 commented 10 months ago

Hi @codenoy,

You can't. The AMQP protocol doesn't specify a reply for channel ack or nack, so RabbitMQ does not reply to confirm the receipt of positive or negative manual acknowledgements. It's a similar problem to what would happen if your application crashed just after completing the task but before sending the acknowledgement.

The solution is for your application to tolerate redeliveries. There's a good article discussing it here

When manual acknowledgements are used, any delivery (message) that was not acked is automatically requeued when the channel (or connection) on which the delivery happened is closed. This includes TCP connection loss by clients, consumer application (process) failures, and channel-level protocol exceptions (covered below).

Note that it takes a period of time to detect an unavailable client.

Due to this behavior, consumers must be prepared to handle redeliveries and otherwise be implemented with idempotence in mind. Redeliveries will have a special boolean property, redeliver, set to true by RabbitMQ. For first time deliveries it will be set to false. Note that a consumer can receive a message that was previously delivered to another consumer.

cressie176 commented 9 months ago

@codenoy OK to close?