Windows Non-WCF Service Moving Transacted MSMQ Message to Failed Queue - c#

I have a legacy Windows service running on Server 2008 that reads messages from a Transactional MSMQ Queue. This is not configured as a WCF service.
We are wanting to improve the handling of failed and poison messages in code (C# 4.0) by catching custom exceptions and sending the related message to a separate 'failed' or 'poison' queue depending upon the type of exception thrown.
I can't get the Catch code to send the message to the separate queue - it disappears from the original queue (as desired!) but doesn't show up in the 'failed' queue.
For testing all of the queues have no Authentication required and permissions are set to allow Everyone to do everything.
Clearly something is missing or wrong and I suspect it is transaction related, but I can't see it. Or perhaps this is not possible the way I am trying to do it ?
Any guidance / suggestions appreciated!
Our simplified PeekCompleted Event code:
private void MessageReceived(object sender, PeekCompletedEventArgs e)
{
using (TransactionScope txnScope = new TransactionScope())
{
MyMessageType currentMessage = null;
MessageQueue q = ((MessageQueue)sender);
try
{
Message queueMessage = q.EndPeek(e.AsyncResult);
currentMessage = (FormMessage)queueMessage.Body;
Processor distributor = new Processor();
Processor.Process(currentMessage); // this will throw if need be
q.ReceiveById(e.Message.Id);
txnScope.Complete();
q.BeginPeek();
}
catch (MyCustomException ex)
{
string qname = ".\private$\failed";
if (!MessageQueue.Exists(qname)){
MessageQueue.Create(qname , true);
}
MessageQueue fq = new MessageQueue(qname){
Formatter = new BinaryMessageFormatter()
};
System.Messaging.Message message2 = new System.Messaging.Message{
Formatter = new BinaryMessageFormatter(),
Body = currentMessage,
Label = "My Failed Message";
};
fq.Send(message2); //send to failed queue
q.ReceiveById(e.Message.Id); //off of original queue
txnScope.Complete(); // complete the trx
_queue.BeginPeek(); // next or wait
}
//other catches handle any cases where we want to tnxScope.Dispose()
EDIT : October 8, 2013
Hugh's answer below got us on the right track. Inside the Catch block the Failed Queue was already created as transactional
MessageQueue.Create(qname , true);
but the Send needed a TransactionType parameter
fq.Send(message2,MessageQueueTransactionType.Single);
That did the trick. Thanks Hugh!

If the message is disappearing from your original queue then that means your code is reaching the second scope.Complete() (in your catch block).
This means the problem has to do with your send to the error queue.
I would suggest that you need to create the queue as transactional because you are sending from within a scope.
MessageQueue fq = new MessageQueue(qname, true){
Formatter = new BinaryMessageFormatter()
};
Then you need to do a transactional send:
fq.Send(message2, Transaction.Current);
See if this works.

Related

Azure Service Bus Receive Messages continuously when ever new message placed in web application [duplicate]

I am using Azure.Messaging.ServiceBus nuget package to work with Azure service bus. We have created a topic and a subscription. The subscription has 100+ messages. We want to read all the message and continue to read message as they arrive.
Microsoft.Azure.ServiceBus package (deprecated now) provided RegisterMessageHandler which use to process every incoming message. I am not able to find similar option under Azure.Messaging.ServiceBus nuget package.
I am able to read one message at a time but I have to call await receiver.ReceiveMessageAsync(); every time manually.
To receive multiple messages (a batch), you should use ServiceBusReceiver.ReceiveMessagesAsync() (not plural, not singular 'message'). This method will return whatever number of messages it can send back. To ensure you retrieve all 100+ messages, you'll need to loop until no messages are available.
If you'd like to use a processor, that's also available in the new SDK. See my answer to a similar question here.
As suggested by #gaurav Mantri, I used ServiceBusProcessor class to implement event based model for processing messages
public async Task ReceiveAll()
{
string connectionString = "Endpoint=sb://sb-test-today.servicebus.windows.net/;SharedAccessKeyName=manage;SharedAccessKey=8e+6SWp3skB3Aedsadsadasdwz5DU=;";
string topicName = "topicone";
string subscriptionName = "subone";
await using var client = new ServiceBusClient(connectionString, new ServiceBusClientOptions
{
TransportType = ServiceBusTransportType.AmqpWebSockets
});
var options = new ServiceBusProcessorOptions
{
// By default or when AutoCompleteMessages is set to true, the processor will complete the message after executing the message handler
// Set AutoCompleteMessages to false to [settle messages](https://learn.microsoft.com/en-us/azure/service-bus-messaging/message-transfers-locks-settlement#peeklock) on your own.
// In both cases, if the message handler throws an exception without settling the message, the processor will abandon the message.
AutoCompleteMessages = false,
// I can also allow for multi-threading
MaxConcurrentCalls = 1
};
await using ServiceBusProcessor processor = client.CreateProcessor(topicName, subscriptionName, options);
processor.ProcessMessageAsync += MessageHandler;
processor.ProcessErrorAsync += ErrorHandler;
await processor.StartProcessingAsync();
Console.ReadKey();
}
public async Task MessageHandler(ProcessMessageEventArgs args)
{
string body = args.Message.Body.ToString();
Console.WriteLine(body);
// we can evaluate application logic and use that to determine how to settle the message.
await args.CompleteMessageAsync(args.Message);
}
public Task ErrorHandler(ProcessErrorEventArgs args)
{
// the error source tells me at what point in the processing an error occurred
Console.WriteLine(args.ErrorSource);
// the fully qualified namespace is available
Console.WriteLine(args.FullyQualifiedNamespace);
// as well as the entity path
Console.WriteLine(args.EntityPath);
Console.WriteLine(args.Exception.ToString());
return Task.CompletedTask;
}

MSMQ poison message not found in queue

Have encounted a strange issue with poisoned messages in an MSMQ queue. When a poisoned message is detected I'm using the code below to handle the exception and move the message to the poison queue, but this fails because the message is not found even though I get its lookupId from the thrown exception. See relevant code below.
public bool HandleError(Exception error)
{
var poisonException = error as MsmqPoisonMessageException;
if (null == poisonException) return false;
var lookupId = poisonException.MessageLookupId;
var queuePath = Environment.MachineName + "\\" + ConfigurationManager.AppSettings["QueuePath"];
var poisonQueuePath = Environment.MachineName + "\\" + ConfigurationManager.AppSettings["PoisonQueuePath"];
var orderQueue = new System.Messaging.MessageQueue(queuePath);
var poisonMessageQueue = new System.Messaging.MessageQueue(poisonQueuePath);
// Use a new transaction scope to remove the message from the main queue and add it to the poison queue.
using (var txScope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
int retryCount = 0;
while (retryCount < 3)
{
retryCount++;
try
{
// Try to get the poison message using the look up id. This line throws InvalidOperationException
var message = orderQueue.ReceiveByLookupId(lookupId);
// Send the message to the poison message queue.
poisonMessageQueue.Send(message, System.Messaging.MessageQueueTransactionType.Automatic);
txScope.Complete();
Logger.Debug("Moved poisoned message with look up id: " + lookupId + " to poison queue: " + ConfigurationManager.AppSettings["PoisonQueuePath"]);
break;
}
catch (InvalidOperationException e)
{
if (retryCount < 3)
{
Logger.Debug("Trying to move message to poison queue but message is not available, sleeping for 10 seconds before retrying", e);
Thread.Sleep(TimeSpan.FromSeconds(10));
}
else
{
Logger.Debug("Giving up on trying to move the message", e);
}
}
}
}
Logger.Info("Restarting the service to process rest of the messages in the queue");
WaitCallback restartCallback = new WaitCallback(Start);
ThreadPool.QueueUserWorkItem(restartCallback);
return true;
}
This code is basically copied from Microsoft's example code here.
The error thrown is of the correct type:
System.ServiceModel.MsmqPoisonMessageException: The transport channel detected a poison message.
But when attempting to get the message from the queue I get:
System.InvalidOperationException: Message requested was not found in the queue specified.
My first thought was that the queues might not have the correct permissions set but I've double checked that the Network Service user has all the necessary rights to read and write messages to both queues.
It's worth mentioning that this code has been working perfectly in production for months, and has survived many poisoned messages in the past. Any input on what might have caused this issue is greatly appreciated.
This will happen when you have more than one retry cycle specified. If your maxRetryCycles is greater than zero and your retryCycleDelay is greater than 30 seconds, you will see the problem you describe. The message is actually sitting in a subqueue called "retry" as it waits the retryCycleDelay between cycles. So when your IErrorHandler looks for it in the "main" queue, it won't find it. For some reason, WCF throws the MsmqPoisonMessageException at the end of each retry cycle, NOT just once at the end of all the retry cycles. Which means that your IErrorHandler will get called at the end of each cycle. Seems really strange to me but that's the way it is.
A better approach now days (if you can guarantee that your code will have MSMQ 4.0) is to change your receiveErrorHandling from "Fault" to "Move" and then get rid of your IErrorHandler. With that approach the messages will be moved for you after all the retries and retry cycles have completed. It is moved to a subqueue called "poison".
See here for more details:
Poison Message Handling in MSMQ 4.0

Azure service bus queue Message deadlettered after Message.Abandon

I am trying out Azure Service Bus queue. I have the below code:
Queue send:
string strConnectionString = ConfigurationManager.AppSettings["Microsoft.ServiceBus.ConnectionString"];
var namespaceManager = NamespaceManager.CreateFromConnectionString(strConnectionString);
if (!namespaceManager.QueueExists("Test"))
{
QueueDescription qD = new QueueDescription("Test");
qD.DefaultMessageTimeToLive = new TimeSpan(05, 00, 00);
qD.LockDuration = new TimeSpan(00, 02, 30);
qD.MaxSizeInMegabytes = 5120;
namespaceManager.CreateQueue(qD);
}
if (namespaceManager.QueueExists("Test"))
{
QueueClient client = QueueClient.CreateFromConnectionString(strConnectionString, "Test", ReceiveMode.PeekLock);
var qMessage = Console.ReadLine();
using (MemoryStream strm = new MemoryStream(Encoding.UTF8.GetBytes(qMessage)))
{
BrokeredMessage bMsg = new BrokeredMessage(strm);
bMsg.MessageId = Guid.NewGuid().ToString();
bMsg.TimeToLive = new TimeSpan(05, 00, 00);
client.Send(bMsg);
Console.WriteLine("Message sent");
}
}
Console.ReadLine();
The receive code:
string strConnectionString = ConfigurationManager.AppSettings["Microsoft.ServiceBus.ConnectionString"];
var namespaceManager = NamespaceManager.CreateFromConnectionString(strConnectionString);
if (namespaceManager.QueueExists("Test"))
{
QueueClient client = QueueClient.CreateFromConnectionString(strConnectionString, "Test",ReceiveMode.PeekLock);
if (client != null)
{
OnMessageOptions options = new OnMessageOptions();
options.AutoComplete = false;
options.AutoRenewTimeout = TimeSpan.FromSeconds(31);
client.OnMessage((message) =>
{
Console.WriteLine(message.State.ToString());
Console.WriteLine("Message Id: " + message.MessageId);
Stream stream = message.GetBody<Stream>();
StreamReader reader = new StreamReader(stream);
Console.WriteLine("Message: " + reader.ReadToEnd());
Console.WriteLine("***************");
message.Abandon();
});
Console.ReadLine();
}
}
I see that whenever I call Abandon, the message is getting DeadLettered. My assumption was that it should get Active and can be picked up by another client.
Your understanding of BrokeredMessage.Abandon Api is correct. It is intended to abandon the peek-lock acquired on the message (but NOT abandon the message itself) and hence, makes it available for other receivers to pick the Message up.
Here's how we envisioned different states of a peek-lock'ed message:
Basics first
The 'Why': If Customers need Competing-Consumer (Job-Queue) semantics - where they need multiple workers to simultaneously process different messages from a Queue with Exactly-Once guarantee - then they use the ReceiveMode.PeekLock. In this model, each worker (the queue receiver) needs a way to communicate the Progress of its Current message (Job) to other workers. Hence, brokeredMessage provides 4 functions to express the states.
The 'What':
if a message is successfully processed by the current Worker - call BrokeredMessage.Complete()
if the BrokeredMessage cannot be processed by the current worker, and want the processing to be retried on another Worker - then, Abandon the message. But, the catch here is: lets say, there are 2 workers and each of them thinks that the other one can process this message and calls Abandon - soon they will end up in an Infinite loop of retry'ing to process just that message! So, to avoid this situation, we provided a Configuration called MaxDeliveryCount on QueueDescription. This setting guards the limit on the number of times the message is delivered from the Queue to receiver. In the above example, Each time you received (and abandoned) the message, the 'deliveryCount' on the ServiceBus service is incremented. When it reaches 10 - the message has hit max no. of deliveries and hence, will be deadlettered.
if the current receiver (worker) knows for sure, that, this message cannot be processed, BrokeredMessage.DeadLetter(). The goal here is to let the consuming application Audit the dead-lettered messages regularly.
if the current receiver (worker) cannot process this message, but, knows that this message can be processed at a later point of time BrokeredMessage.Defer().
HTH!
Sree

Message Queue Exception : Queue does not exist or you do not have sufficient permissions to perform the operation

At this line of code i am getting the error as i mentioned
I declared MSMQ_NAME as string as follows
private const string MSMQ_NAME = ".\\private$\\ASPNETService";
private void DoSomeMSMQStuff()
{
using (MessageQueue queue = new MessageQueue(MSMQ_NAME))
{
queue.Send(DateTime.Now); //Exception raises
queue.Close();
}
}
Can you first verify the queue is existing with the name 'ASPNETService' at below location?
Computer Management -> Services and Applications -> Message Queuing -> Private Queues
I had a similar problem. I was confused because my code worked on my local development machine, but not in production. Even stranger, the queues were created the exact same way.
It turns out that IIS doesn't have access to them by default. I just opened up the permissions.
Computer Management -> Private Queues -> right-click queue name -> Properties -> Security Tab -> click "Everyone" user -> click Full Control/Allow checkbox -> click OK
This fixed it for me, and in my case it's not an issue, but you may want to think about the ramifications of just opening it up for all users.
Also, I had to do this across all queues on all servers. There doesn't seem to be a way to multi-select queues or folders in order to set permissions for multiple queues simultaneously.
I was having the same problem.
I had created a new private queue and gave Full Permission to Everyone.
But I was still catching a "Queue does not exist or you do not have sufficient permissions to perform the operation" when trying to Send() to the queue. And I was able to verify that MessageQueue.Exists(".\\private$\\myqueue") was returning true.
Restarting the Message Queuing Service resolved my the problem for me.
I had same problem and I did like below where I check whether queue exists or not. If yes send message else create queue and then send message
MessageQueue msgQueue = null;
string queuePath = ".\\Private$\\billpay";
Payment newPayment = new Payment()
{
Payee = txtPayee.Text,
Payor = txtPayor.Text,
Amount = Convert.ToInt32(txtAmount.Text),
DueDate = dpDueDate.SelectedDate.Value.ToShortDateString()
};
Message msg = new Message();
msg.Body = newPayment;
msg.Label = "Gopala - Learning Message Queue";
if (MessageQueue.Exists(queuePath) == false)
{
//Queue doesnot exist so create it
msgQueue = MessageQueue.Create(queuePath);
}
else
{
msgQueue = new MessageQueue(queuePath);
}
msgQueue.Send(msg);
I was facing the same problem, I had resolved it using the following class to create queue
private MessageQueue messageQueue;
public const string DEFAULT_QUEUE_NAME = "newQueue";
public const string QUEUENAME_PREFIX = ".\\Private$\\";
public static string QueueName
{
get
{
string result = string.Format("{0}{1}", QUEUENAME_PREFIX, DEFAULT_QUEUE_NAME);
return result;
}
}
public void SendMessage()
{
string queuePath = QueueName;
MessageQueue messageQueue = MessageQueue.Create(queuePath);
messageQueue.Send("msg");
}
Create message queue in same manner for receiving the message.
For others struggling with this and pulling their hair out like I have been, I finally found something that works when all of the upvoted suggestions failed.
Even if you think the host name of your target queue's hosting system is being resolved correctly, don't believe it. Try replacing the host name with an IP address and see if it works. It does for me. I can WRITE to a public queue using a host name on my remote server without problems, but trying to READ from it produces exactly the error listed for this question.
For example, if I declare the following:
private static string QueueName = #"FormatName:DIRECT=TCP:SOMEHOST\MyQueue";
private static System.Messaging.MessageQueue Queue = new System.Messaging.MessageQueue(QueueName);
Where "MyQueue" is a public queue on server SOMEHOST, the following code will successfully insert messages to the queue, but always fails on the Receive():
Queue.Formatter = new XmlMessageFormatter(new Type[] { typeof(String) });
// The Receive() call here is a blocking call. We'll wait if there is no message in the queue, and processing
// is halted until there IS a message in the queue.
//
try
{
Queue.Send("hello world", System.Messaging.MessageQueueTransactionType.Single);
var msg = Queue.Receive(MessageQueueTransactionType.Single);
}
catch (Exception ex)
{
// todo error handling
}
One simple change in how I specify the queue location is all that's needed to make the Receive() stop failing with the dreaded "queue does not exist or you do not have sufficient permissions" error:
private static string QueueName = #"FormatName:DIRECT=TCP:192.168.1.100\MyQueue";
(Obviously I've obfuscated IP addresses and other sensitive info). Using the IP address is not obviously a production-worthy scenario, but it did point me to some type of name resolution problem as being the possible cause of the error. I cannot explain why Send() works but Receive() does not when I am using a host name instead of IP, but I can reproduce these results consistently. Until I can figure out what's going on with the name resolution, I'm no longer wasting a day trying to read messages from a queue.

Receiving MSMQ messages with Windows Service

I'm creating a Windows Service in C#.
What is the best way to listen for messages?? How do I code this properly??
You don't listen. You configure MSMQ Activation to activate your component when messages arrive. The link has all the details you need, code and configuration.
As previously stated, MSMQ Activation is probably the best way, if you can use that. Alternatively, here is code that I've used:
var ts = new TimeSpan(0, 0, 10);
MessageQueue q = GetQueue<T>();
while (true)
{
try
{
Message msg = q.Receive(ts);
var t = (T)msg.Body;
HandleMessage(t);
}
catch (MessageQueueException e)
{
// Test to see if this was just a timeout.
// If it was, just continue, there were no msgs waiting
// If it wasn't, something horrible may have happened
}
}

Categories

Resources