Tuesday, 3 November 2015

NServiceBus, DTC and MSMQ

I've spent the last week performance testing our NServiceBus solution extensively.

Our first implementation used MSDTC.
During performance testing of an NServiceBus application using MSMQ transport, we managed to kill MSDTC many times. It would go into a period of hanging for 20 minutes and everything around it; including endpoints not owned by us, stopped working.

We turned off enlistment of the databases called by the handlers by altering the connection strings. This still didn't stop MSDTC from hanging.

Analysis of the stack traces showed that NServiceBus was still engaging MSDTC through the use of TransactionScope around the handler. Although we have switched DisableDistributedTransactions = On, the handlers were still using the local transaction coordinator. We had to set DoNotWrapHandlersExecutionInATransactionScope = true.

The different settings are:
1) DisableDistributedTransactions = off, DoNotWrapHandlersExecutionInATransactionScope = off . The entire handler will be wrapped in an outer transaction scope. Any Bus.Sends will not be immediate and will wait till the outer transaction scope completes. In case of a rollback of the outer transaction scope all the Bus.Sends will be rolled back as well as any database work enlisted in the transaction scope will roll back.

2) DisableDistributedTransactions = on, DoNotWrapHandlersExecutionInATransactionScope = off. The entire handler will be wrapped in an outer transaction scope. Any Bus.Sends will be immediate and will not participate in the outer transaction scope. In the case of a rollback of the outer transaction scope the bus.sends will not roll back but any database work in the handler will roll back.
3) DisableDistributedTransactions = off, DoNotWrapHandlersExecutionInATransactionScope = on. The handler will not be wrapped in an outer transaction scope. I will explicitly have to start a transaction scope and any bus.sends will not be immediate (they completed upon outer transaction scope complete) and database work done within the manually started one will rollback together. Anything outside of my manually created transaction scope will not rollback.

4) DisableDistributedTransactions = on, DoNotWrapHandlersExecutionInATransactionScope = on. The handler will not be wrapped in an outer transaction scope. Each bus.send will be within its own transaction and if a transaction scope is created manually by me those bus sends will not enlist in it will execute immediately and will not roll back if the outer transaction scope rolls back. Any db work done within the manual transaction scope will roll back.

https://groups.google.com/forum/#!msg/particularsoftware/SoxeC4RwLlg/Kdt-gjTXYEIJ

Having changed this we were now using TransportTransactions. MSMQ still can handle a Receive and send in a transaction, without using DTC.













MSMQ uses memory-mapped files to store messages (regardless of whether they are express, recoverable, durable). It uses a 4MB file for every message and cleans them up every 6 hours. If you see the memory go very high you can initiate the cleanup by running the following PowerShell:

$MyMSMQApp = new-object  –comObject  MSMQ.MSMQApplication
$MyMSMQApp.Tidy()

No comments:

Post a comment