amqp-node / amqplib

AMQP 0-9-1 library and client for Node.JS

Home Page:https://amqp-node.github.io/amqplib/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unable to cancel consumers

pastTenseVerb opened this issue · comments

I'm unable to cancel a consumer. Pretty simple usecase, I'd like to stop a consumer and spin up another one since my batch size is based on bytes instead of message count. I'd like to consume, stop consuming, send off the data asynchronously, and resume consumption. Here's a simple example to test out cancel abilities, but I can't seem to cancel the consumer. I have over 500 messages in a queue running locally and all of them are consumed using this snippet that is very similar to this issue: #723

I'm terrible at editing code in github issues, please bare with me!

async function connectFunction() {
  try {
    const connection = await connect({
      protocol: "amqp",
      hostname: "localhost",
      port: 5672,
      username: "test",
      password: "testPassword",
    });

    const channel = await connection.createChannel();
    await channel.assertQueue("hello", { durable: false });

    await channel.consume("hello", async (msg) => {
      const consumerID =  msg.fields.consumerTag
      let message = msg.content.toString();
      console.log("content", message, consumerID);
      await channel.cancel(consumerID);
    });
  } catch (error) {
    console.log("error from test", error);
  }
}
connectFunction();

I end up console.loging all 500 messages back to back. It seems like the cancel call doesn't go through or can't be called from within the consume method's callback. If anyone has run into this or has any clarification I'm welcome to hear it!

Hi @pastTenseVerb,

It looks like you are not setting a channel prefetch. This means that RabbitMQ will deliver all messages concurrently, hence the messages will have all been delivered before you cancel the consumer. Instead you need to either switch to using channel.get or set a channel prefetch.

When I add a prefetch of 50, it does stop all 500 from getting consumed but I'm still consuming 50 after I call channel.cancel() so not much has changed. I can switch to using channel.get but are there any drawbacks to doing so?

Also do you think it's possible I'm getting false positives by running a rabbitMQ container locally? I'm sure channel.cancel works as intended since there aren't many issues brought up about it.

When you set prefetch to 50, you are telling RabbitMQ deliver messages concurrently until there are 50 unacknowledged messages for this consumer. By the time you have called channel.cancel(), Rabbit has already delivered 50 messages. Cancelling will only stop subsequent messages from being delivered.

To achieve what I think you want, you need to set the prefetch to 1. Then you probably don't need to cancel the consumer, since you will only be handling one message at a time. The alternative is to explicitly get a message from the channel. The documentation can be found here. The drawback is when the queue is empty, you will have to decide how frequently to poll.