Automated purging of Dead-lettered messages in different Service Bus namespaces in Azure
By Anshul
- 4 minutes read - 685 wordsOverview
Usually when we work with Service Bus queues, after a while, we end up with some messages reaching the corresponding Dead-letter queue, which is nothing but a partition inside that queue to store messages that weren’t successfully processed. The reason for the messages ending up in DLQ could be anything starting from lock-duration issue to max delviery count threshold or even a technical issue. Once we have these messages piled up in DLQ, next action should be try to reprocess. If that is not possible, then we may have to purge these DLQs manually by using an application called Service Bus Explorer. See the details here. If you are dealing with a bigger integration, where you have tens of queues in different namespaces, then doing this cleanup task could be exhausting. Service Bus explorer does help in purging all the DLQ messages in a single namespace ins one-go, but again you would have to connect to each namespace first in the application. Also, if you want to exclude few of the queues or purge only those messages which are older than let’s say 30 days, then you would need a customized solution.
Script
I recently came across this scenario where I had to purge few DLQs completely while keeping the last 60 days messages in the rest of them. As we know, accessing (Read) the Queue messages can only be done using either Service bus Explorer or SDKs. In my script, I am utilising powershell to authenticate against my Azure account and then calling a local Azure function to recieve all the messages one-by-one (which ultimately removes the messages from the queue).
Note: If you just want to read the messages without deleting them from the queue, use Peek as Receive Mode instead.
Here is how the powershell script looks like-
$sbnames = Get-AzServiceBusNamespace | Select-Object -Property Name,ResourceGroupName | Out-GridView -PassThru
foreach($sbname in $sbnames){
$queueList = Get-AzServiceBusQueue -ResourceGroupName $sbname.ResourceGroupName -Namespace $sbname.Name | Select-Object -Property Name
$key = Get-AzServiceBusKey -Namespace $sbname.Name -ResourceGroupName $sbname.ResourceGroupName -Name "RootManageSharedAccessKey"
$Body = @{
"cs" = $key.PrimaryConnectionString
}| ConvertTo-Json
foreach($queue in $queueList){
$uri = "http://localhost:7071/api/ClearDLQFunction/" + $queue.name
Invoke-WebRequest -Uri $uri -Method Post -Body $Body -ContentType application/json
}
[System.Windows.MessageBox]::Show($sbname + ' done') #very much optionals
}
And below is the code used in Azure function-
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
string cs = data.cs;
string deadLetterQueueName = queue + "/$DeadLetterQueue";
var sbClient = new ServiceBusClient(cs);
{
var rcv = sbClient.CreateReceiver(deadLetterQueueName);
CancellationTokenSource cancellationToken = new CancellationTokenSource();
for(var i =0; i < 5000; i++)
{
ServiceBusReceivedMessage deadLetter = await rcv.ReceiveMessageAsync(null, cancellationToken.Token);
if (deadLetter != null && deadLetter.EnqueuedTime >= DateTime.Now.AddDays(-30))
{
log.LogInformation($"reason: " + deadLetter.DeadLetterReason);
await rcv.CompleteMessageAsync(deadLetter, cancellationToken.Token);
}
else
{
return new OkObjectResult(new OkResult());
}
}
await rcv.CloseAsync();
return new OkObjectResult(new OkResult());
}
Note: queue + “/$DeadLetterQueue”; is the only way you can access a Dead-letter queue. You have to append $DeadLetterQueue in the queue name you are passing.
How to Run
There are few things you need to do before running this script-
Save Powershell script and create a local Azure function with the code shown above.
Run the function and then open ISE and then the Powershell script inside it.
Next you need to run Connect-AzAccount to login and then select your appropriate subscription.
Once all that is done, Run the powershell script and sit tight. It will pop-up a window for you to select the Service Bus namespaces you want to run this script against. Select all if needed. You can do a similar setup if you want to choose the queues before deleting the messages. And once the script purges the queues of one Service Bus, it will pop up a message window saying SBName done. Isn’t that cool?!
#Remember: If you need to remove all the Dead-lettered messages irrespective of their queued time, then you can remove the condition in the If block in the function code.
Note: This is not a perfect solution to achieve the purpose, but still a work in progress. I will try and keep updating it as and when I get some time.
So that’s it. Hope it helps!