weloytty / MSMQ.Messaging

A drop in replacement for System.Messaging for use with .NET Core (on Windows only)
MIT License
38 stars 14 forks source link

Implement a delay in C# MessageQueue using MSMQ #13

Open popohoma opened 1 month ago

popohoma commented 1 month ago

Hi All,

I am currently facing a technical problem dealing with MSMQ, C# MessageQueue and Thread.

My system is using MSMQ and C# MessageQueue framework. At the moment it will read message one by one from MSMQ and process it in C#.

There is a business scenario that the message that I am processing will be relying on another message that comes in later (say 5 minutes later). Therefore I need process this message later after the dependant message has persisted into database.

I want to put the original processing message back to the Queue with delay, retry for 10 times max with 10 seconds interval each. What is the best way for me to implement this logic given my current module is single threaded. It reads message one by one from the Queue in FIFO order.

Current Code:

var message = new System.Messaging.Message(messageBody, formatter)
_myQueue.Send(message)

I am thinking to use a ThreadPool to manage any messages requires a delay but my colleagues strongly against it as current module is single threaded and my separate thread may have adverse effect to it.

Your suggestion is greatly appreciated.

MSMQ

thomasjohngleeson commented 1 month ago

We implemented a set of retry queues to accomplish something similar.

Note: none of our messages are order dependent and are idempotent. If while processing a message the handler throws a retriable exception (ie: database deadlock/timeout) the handler code sends the 'bad' message to the retry queue and commits the transaction which threw the exception (ie: pops the 'bad' message). A separate service is responsible for monitoring the retry que and routing messages to one of 4 'delay' queues. If this is the first visit of a message to the retry service then it gets routed to the 30 second retry queue, second try goes to the 60 second queue and so forth. That same service also has a poller thread which peeks each queue every 15s looking for expiry times and then popping those which have expired and routing back to their destination queue. Note: using separate queues keeps the peeking somewhat efficient because the peek never needs to beyond the first message to see if any have expired, its effectively FIFO.

If we needed a static 5 min delay queue like your use case, I think we could have the sender send directly to a 300s 'retry' queue and our retry service would do the right thing. IJW :)