0

We have a WCF service that listens for messages on a queue (MSMQ). It sends a request to our web server (REST API), which returns an HTTP status code.

If the status code falls within the 400 range, we are throwing away the message. The idea is a 400 range error can never succeed (unauthorized, bad request, not found, etc.) and so we don't want keep retrying.

For all other errors (e.g., 500 - Internal Server Error), we have WCF configured to put the message on a "retry" queue. Messages on the retry queue get retried after a certain amount of time. The idea is that the server is temporarily down, so wait and try again.

The way WCF is set up, if we throw a FaultException in the service contract, it will automatically put the message on the retry queue.

When a message causes a 400 range error, we are just swallowing the error (we just log it). This prevents the retry mechanism from firing; however, it would be better to move the message to a dead-letter queue. This way we can react to the error by sending an email to the user and/or a system administrator.

Is there a way to immediately move these bad messages to a dead-letter queue?

Travis Parks
  • 8,435
  • 12
  • 52
  • 85
  • I know you want to use WCF, but what you ask can easily be done if you just use the **MessageQueue** class. There is much in MSMQ that WCF prevents you from using. http://msdn.microsoft.com/en-us/library/system.messaging.messagequeue.aspx –  Jun 07 '14 at 12:35

2 Answers2

1

First, I kept referring to the dead-letter queue. At the time when I posted this question, I was unaware that WCF/MSMQ automatically creates what's known as a poison sub-queue. Any message that can't be delivered in the configured number of times is put in the poison sub-queue.

In my situation, I knew that some messages would never succeed, so I wanted to move the message out of the queue immediately.

The solution was to create a second queue that I called "poison" (not to be confused with the poison sub-queue). My catch block would create an instance of a WCF client and forward the message to this poison queue. I could reuse the same client to post to both the original queue and the poison queue; I just had to create a separate client end-point in the configuration file for each.

I had two separate ServiceHost instances running that read the queues. The ServiceHost for the original queue did the HTTP request and forwarded messages to the poison queue when unrecoverable errors occurred. The second ServiceHost would simply send out an email to record that a message was lost.

There was also the issue of temporary errors that exceeded the maximum number of tries. WCF/MSMQ automatically creates a sub-queue called <myqueuename>;poison. You cannot directly write to a sub-queue via WCF, but you can read from it using a ServiceHost. Whenever messages end up in the poison sub-queue, I simply forward the message to the poison queue, with the exact same client I use in the original handler's catch block.

I wanted the ability to include a stack trace in the error emails. Since I was reusing the same client and service contract for all of the handlers, I couldn't just pass along the stack trace as a string (unless I added it to all of my data contracts). Instead, I had the poison handler try to execute the code one more time, which would fail again and spit out the stack trace.

This is what my message queues ended up looking like:

MyQueue
    - Queue messages
    - Retry
    - Poison
MyQueuePoison
    - Queue messages

This approach is pretty convoluted. It was strange calling A WCF client from within a WCF service handler. It also meant setting up one more queue on the server and a ton of additional configuration sections for specifying which queue a client should forward messages to.

Travis Parks
  • 8,435
  • 12
  • 52
  • 85
0

hopefully I have understood your question and if it is what i think you are saying then yes there is but you obviously need to program it to do this. But you DO need a retry amount set so the MSMQ can retry until it gives up. Or you can create your own custom queue for dead letters/messages

http://msdn.microsoft.com/en-us/library/ms789035(v=vs.110).aspx http://msdn.microsoft.com/en-us/library/ms752268(v=vs.110).aspx

take a look here also:

http://www.michaelfcollins3.me/blog/2012/09/20/wcf-msmq-bad-message-handling.html

How do I handle message failure in MSMQ bindings for WCF

I hope these links help.

Community
  • 1
  • 1
Ahmed ilyas
  • 5,722
  • 8
  • 44
  • 72
  • So, normally, a message is only retried a specified number of times based on the `MaxRetryCycles` property. But in my case there is no point in retrying. I want to go immediately to the dead-letter queue. – Travis Parks May 30 '14 at 19:24
  • I was wonder if I had to manually create another end-point to the dead-letter queue, create another service contract and create another client and call it explicitly within the 400 handling code. Seems like a lot to just go directly to the dead-letter queue. – Travis Parks May 30 '14 at 19:26
  • yes I don't think you can move directly to the dead letter queue. Maybe you should set a max retry cycle of just 1 - whats the harm in retrying? it failed the first time so it may fail the second time or it may succeed (which is good) – Ahmed ilyas May 30 '14 at 19:51
  • @Ahmedilyas If you set the max retry to 1 that will affect **all messages** even ones that might fail once but given the chance may succeed next time. A retry of 1 not only prevents the 400 requests but also severely limits the use-fullness of MSMQ –  Jun 07 '14 at 12:33
  • @MickyDuncan - thanks. I was under the impression that the retry would retry messages if they failed to deliver - simple as. – Ahmed ilyas Jun 07 '14 at 13:03
  • @Ahmedilyas yes, but in this case just once. In the grand scheme of things, a retry of "1" is generally not useful. See _JMS; ApacheMQ; EIA Patterns_ for examples –  Jun 07 '14 at 14:43