We have noticed an issue with message handlers that have a long running request, if the request does not finish processing before the distributed transaction times out the same message is reprocessed on a new thread before the original message is done processing or before the original message even throws an error. The message does eventually get to the error queue.
Here is how we reproduced the issue:
Updated local machine config to a default timeout of 1 minute for transactions:
<system.transactions>
<defaultSettings distributedTransactionManagerName="" timeout="00:01:00" />
<machineSettings maxTimeout="00:01:00" />
</system.transactions>
Configured NSB message service to be multi threaded:
<MsmqTransportConfig NumberOfWorkerThreads="2" MaxRetries="2" />
Then in our handler did a thread sleep for 2 minutes (code below), what happens is that a message comes in on worker tread A. Once the 1 minute timeout occurs the same message gets handled on worker thread B and worker thread A is still sleeping. Worker thread A eventually comes back and errors with “Cannot enlist in transaction.” and then restarts processing the message again and this continues on both worker threads till the message eventually gets into the error queue.
public void Handle(RequestDataMessage message)
{
Logger.Info("==========================================================================");
Logger.InfoFormat("Received request {0}.", message.DataId);
Logger.InfoFormat("String received: {0}.", message.String);
Logger.InfoFormat("Header 'Test' = {0}.", message.GetHeader("Test"));
Logger.InfoFormat(Thread.CurrentPrincipal != null ? Thread.CurrentPrincipal.Identity.Name : string.Empty);
var response = Bus.CreateInstance<DataResponseMessage>(m =>
{
m.DataId = message.DataId;
m.String = message.String;
});
response.CopyHeaderFromRequest("Test");
response.SetHeader("1", "1");
response.SetHeader("2", "2");
Thread.Sleep(new TimeSpan(0, 2, 0));
Logger.Info("========== Thread continued ===========");
Logger.InfoFormat("Received request {0}.", message.DataId);
Logger.InfoFormat("String received: {0}.", message.String);
Logger.InfoFormat("Header 'Test' = {0}.", message.GetHeader("Test"));
Logger.InfoFormat(Thread.CurrentPrincipal != null ? Thread.CurrentPrincipal.Identity.Name : string.Empty);
Bus.Reply(response); //Try experimenting with sending multiple responses
}
Is this a know issue, or is there a way we should design our code to avoid this type of intensive reprocessing that occurs?