Search code examples
azureazureservicebusazure-servicebus-queues

Complete a message in a dead letter queue on Azure Service Bus


I want to be able to remove selected messages from my deadletter queue.

How is this accomplished?

I constantly get an error:

The operation cannot be completed because the RecieveContext is Null

I have tried every approach I can think of and read about, here is where I am now:

public void DeleteMessageFromDeadletterQueue<T>(string queueName, long sequenceNumber)
        {
     var client = GetQueueClient(queueName, true);
                var messages = GetMessages(client);
                foreach(var m in messages)
                {
                    if(m.SequenceNumber == sequenceNumber)
                    {
                        m.Complete();
                    }
                    else
                    {
                        m.Abandon();
                    }
                }
}

/// <summary>
        /// Return a list of all messages in a Queue
        /// </summary>
        /// <param name="client"></param>
        /// <returns></returns>
        private IEnumerable<BrokeredMessage> GetMessages(QueueClient client)
        {
            var peekedMessages = client.PeekBatch(0, peekedMessageBatchCount).ToList();
            bool getmore = peekedMessages.Count() == peekedMessageBatchCount ? true : false;

            while (getmore)
            {
                var moreMessages = client.PeekBatch(peekedMessages.Last().SequenceNumber, peekedMessageBatchCount);
                peekedMessages.AddRange(moreMessages);
                getmore = moreMessages.Count() == peekedMessageBatchCount ? true : false;
            }

            return peekedMessages;
        }

Not sure why this seems to be such a difficult task.


Solution

  • The issue here is that you've called PeekBatch which returns the messages that are just peeked. There is no Receive context which you can then use to either complete or abandon the message. The Peek and PeekBatch operations only return the messages and does not lock them at all, even if the receivedMode is set to PeekLock. It's mostly for skimming the queue, but you can't take an action on them. Note the docs for both Abandon and Complete state, "must only be called on a message that has been received by using a receiver operating in Peek-Lock ReceiveMode." It's not clear here, but Peek and PeekBatch operations don't count in this as they don't actually get a receive context. This is why it fails when you attempt to call abandon. If you actually found the one you were looking for it would throw a different error when you called Complete.

    What you want to do is use a ReceiveBatch operation instead in PeekBatch RecieveMode. This will actually pull a batch of the messages back and then when you look through them to find the one you want you can actually affect the message complete. When you fire the abandon it will immediately release the message that isn't what you want back to the queue.

    If your deadletter queue is pretty small usually this won't be bad. If it is really large then taking this approach isn't the most efficient. You're treating the dead letter queue more like a heap and digging through it rather than processing the messages "in order". This isn't uncommon when dealing with dead letter queues which require manual intervention, but if you have a LOT of these then it may be better to have something processing the dead letter queue into a different type of store where you can more easily find and destroy messages, but can still recreate the messages that are okay to push to a different queue to reprocess.

    There may be other options, such as using Defer, if you are manually dead lettering things. See How to use the MessageReceiver.Receive method by sequenceNumber on ServiceBus.