How to delete a specific message from RabbitMQ Queue ?

How to delete a specific message from RabbitMQ Queue ?

Recently I was looking for a way to delete a specific message from a RabbitMQ. After reading some threads online How to selectively delete messages from an AMQP (RabbitMQ) queue?, How to remove specific message from queue in rabbitmq, RabbitMQ: Is it possible to remove a message after it is queued? and RabbitMQ documentation I realized that :

  • Messages should not stays in queues, this is not the main purpose of RabbitMQ to store messages, it’s more to deliver messages. So having many undelivered messages (so you need to delete one of them) is not a good thing.
  • RabbitMQ does not have an API to delete a specific message whitin a queue.

Ok, so I understand all of this, I also understand that maybe I made a design error, but now I have that queue with hundreds of messages and I would like to delete some of them. What are my solutions ?

Consume Acknowledge Requeue

The first idea I had was to create a consumer for the queue with message, if message is the one I want to delete I acknowledge it, if not I requeue the message. The main problem with this solution is that you will create a loop, the requeued message will be consumed and requeued and so on.

So we need to break the loop, for that you will need a way to identify the messages with an id, RabbitMQ does not have that kind of feature by default, but when producing a message you can define a message_id property, this is application identifier (not RabbitMQ), personnaly I used a GUID to define this property.

If you have an identifier or any way to identify a message you can continue with this solution, if you don’t have you may choose another solution. Store all message in Database (and empty queue) or Consume then Queue then Dead Letter.

⚠️ The message identifier is needed to prevent looping, not for identifying the message to delete (even if it can).

So let’s imagine the solution, create a consumer for the queue with messages (create a confirmChanel), for each message, check if the message has been received already, if not, store the identifier of the message, check if it should be deleted, if it should, then acknowledge it, if not then requeue it. If the message has been received already, then you can stop the consumer.

I wrote a small javascript tool to delete a message from a queue by its identifier (the message_id property). rabbitmq-delete-message. It is simple to use :

const deleteMessage = require('rabbitmq-delete-message');
const serverURL = 'amqp://localhost:15672';
 
deleteMessage(serverURL, 'MY_QUEUE', '6389503c-0281-412e-82cb-e92c97281b59')
  .then((response) => {
    if (response.deleted) {
      console.log('Message was deleted');
      console.log(response.message);
    } else {
      console.log('Message was not found');
    }
  })
  .catch((err) => console.log(err));

Store all message in Database (and empty queue)

If I have hundreds of messages in a queue, and if I want to delete only some of them, mean I want to save all other messages. And if you want to store data, why not using a Database. With a Document Database (such as MongoDB) it can be very easy to create a consumer for the queue, read all messages and store them in the database.

For the messages you want to delete you can simply ignore them in the consumer, or of course delete them from the database, as you can use queries to do that !

I think it may be the best solution, but in my case it was not possible to use a database.

Consume then Queue then Dead Letter

The idea here is to use the Dead Lettering feature.

First we need to create a Dead Letter Exchange, this exchange should be bound to the queue with message.

Then create a new filtered messages queue for the messages we want to keep, this queue should be created with a dead letter exchange and a TTL of your choice.

Then we create a consumer for the queue with messages, and this consumer will push the messages to the filtered messages queue if it should be kept.

As the filtered messages queue, has a dead letter exchange, after the time defined in the TTL, the message will be send to the Dead Letter Exchange and store again to the queue with message.

To avoid looping, you should stop the consumer before the TTL.

Conclusion

I think it is good practice to specify the message_idproperty, it can also increase traceability between consumers and producers. It can be very helpfull in a Micro Services environment.

Some times ago I wrote a library to send event in node using RabbitMQ (rabbitmq-event-manager), this library will set the message_id property with a GUID, Maybe it can help you also.

I hope this post helped you ! Feel free to leave any comments or suggestions.