Using PowerShell to Remove Poisonous Messages
At work we ran into an issue where a few messages that were being written from an app running our logging framework (which sits on top of Enterprise Library using MSMQ) was pointed at an incorrect queue. When the process that reads off the queue and inserts into the database (the Logging Distributor) hit the first of these messages it failed attempting to case the log entry to the serialized object it was expecting, at which point it logged an error in the Event Log of the server and then shut down the service. Messages that can’t be processed off a queue are referred to as Poisonous messages and we had a few of them in the queue. It would have been nice if it had retried the operation once and then put it into a “dead letter” queue, but the service just isn’t written to do that (maybe I can find time to remedy that).
The only option we currently have to deal with these poisonous messages is either the purge the entire queue (and lose all the good messages as well), or identify and remove the poisonous messages.Even though this happened in our development environment people are more likely looking in the logging database for these messages as they test and code their features, so just purging the queue could cause other developers to spend time trying to figure out where their logging messages went. In production we most certainly would NOT want to just purge the queue.
To fix the issue we used a bit of PowerShell to run through the queue, identify the messages and then pull them off the queue. Here is the code:
[Reflection.Assembly]::LoadWithPartialName("System.Messaging" )
$q = new-object -TypeName System.Messaging.MessageQueue -ArgumentList ".\sfglogging4.0"
$q | ? { $_.Label -eq "Logging Unit Tests" } | % { $q.ReceiveById($_.Id) }
We ran this from the PowerShell prompt on the server hosting the queue. In this case the label on the messages was “Logging Unit Tests”, which for the logging block from Enterprise Library translates to the Title of the app doing the logging.
If you are interested the code is doing the following:
1st Line: Loads the System.Messaging assembly into the PowerShell console so that it can be used.
2nd Line: Created a new MessageQueue object using the constructor parameter of the queue name.
3rd Line: This one is a little terse, but it is taking the queue ($q) and piping all of the messages on that queue through a filter (? translates to Where-Object) to get all of the messages that have a Label equal to what we are looking for, then finally it pipes the filtered messages to a for each (% translates to ForEach-Object) that calls ReceiveById off the queue using the ID of the message. This effectively removes the message from the queue.
If we just wanted to remove the very first message in the queue (if we thought only it had an issue) you could replace line 3 above with:
$q.Receive()
That will pull the very first message.
This was pretty quick in that it went through 9,000 entries and removed the poison messages in just a few seconds.