amqp-node / amqplib

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

Set a custom header before nack-ing a message #735

Closed dimisus closed 1 year ago

dimisus commented 1 year ago

If the above does not help, please provide as much of the following information as is relevant...

I desperately want to set a header for a message that is going to be nack'ed. But apparently it is impossible to set a custom header on a message since nack only uses a reference to a message. Is there any work around or any possibility I am not able to find?

What I want to achieve is to set a hint of a reason why the message was nack'ed as a custom header. Which would make debugging and processing of nack'ed message in production much easier and one can react to issues faster.

If I go the hacky way and publish the messages directly to the DLX I may lose a lot of built-in functionality such as the x-death headers etc.

 class MessageBroker ... {

  ...

  nack(id: number | string, queueName: string, reason: string | null = null): void {

    // get the original message from local store
    const originalMessage = this.actives[id];

    // set a header to the message payload
    if (reason && typeof reason === 'string') {
      originalMessage.properties.headers = {
        ...originalMessage.properties.headers,
        'x-nack-reason': reason.substring(0, 63),
      };
    }

    // nack the message using the appropriate channel
    this.channels[queueName].nack(originalMessage, false, dlx);

    delete this.actives[id];
  }
cressie176 commented 1 year ago

Hi @dimisus,

Unfortunately there is no good way to do this with ~amqp or RabbitMQ~ amqplib. As you say, the only way is to republish the message.

Rascal (a wrapper for amqp) has the option of republishing the message back to the original queue after copying the headers and adding a failure reason. It also adds a special header instructing Rascal to immediately nack the message on redelivery rather than attempting to process it.

You could do something similar, but it still feels hacky and creates the potential for a duplicate message since the original message is not acknowledged/discarded before the mutated one is republished.

dimisus commented 1 year ago

Hi. Thank you for the quick reply. Pity...

carlhoerberg commented 1 year ago

You can enable transaction on the channel, and then republish the message and ack the original message in a single transaction.

dimisus commented 1 year ago

Beg you pardon, but not it is not obvious how to:

https://amqp-node.github.io/amqplib/channel_api.html

I am thinking about republishing the message using the delayed message plugin to force it for another full cycle.

cressie176 commented 1 year ago

Unfortunately transactions aren't supported with amqplib. There's a discussion here.