I'm writing functionality for monitoring and fixing messages in the dead letter queue of a topic subscription on Azure.
I can get a list of messages using _subscriptionClient.PeekBatch(10) for display; however we are stuck when we want to actually remove one of the messages.
The workflow would be:
- Get a list of all messages
- Send a specific message (say the 5th on in the collection) back to the original queue for reprocessing
- Mark the associated dead letter message as
.Complete().
The issue is that whilst we have the list of messages, we cannot call .Complete() on a specific one without using .subscriptionClient.Receive() first.
As you cannot .Receive() a message by MessageId, does this mean we have to loop through the messages one by one like below?
public BrokeredMessage GetMessageById(string messageIdentifier)
{
BrokeredMessage matchingMessage = null;
var messageNotFound = true;
var messagesToAbandon = new List<BrokeredMessage>();
while (messageNotFound)
{
var message = _subscriptionClient.Receive();
if (message == null)
{
throw new Exception("Could not find the message on the queue");
}
if (message.MessageId == messageIdentifier)
{
messageNotFound = false;
matchingMessage = message;
}
else
{
messagesToAbandon.Add(message);
}
}
// Unlock all messages that do not match the matching one received.
foreach (var message in messagesToAbandon)
{
message.Abandon();
}
return matchingMessage;
}
My issue with this approach is:
- It's not scalable; what if the message is the 100th in the collection? We'd have to loop through 99 and mark each as abandoned
- We risk losing the lock on the
matchingMessageif there are too many items to abandon - It's a long running process
I've toyed with the idea of marking each message as abandoned if not matched within the loop; however this creates the risk of us looping through the same items infinitely (as .Abandon() puts them back on the queue).
Has anyone found an efficient way around this? Maybe using .Defer() with .Receive(sequenceNumber)?