Geeks With Blogs

@adrianhara
  • adrianhara @DellCares hey Dell, I bought a monitor from a reseller that went bankrupt and now the monitor died (still under warranty)...what can I do? about 412 days ago
  • adrianhara @nlaplante heh, i implemented something myself yesterday and submitted a pull request...looking forward to comparing the two about 418 days ago
  • adrianhara @nlaplante hey, is there a way to load "self-contained" info window content for the angularmap directive? (load template html+controller)? about 420 days ago
  • adrianhara well lol, apparently Microsoft customer satisfaction surveys expire after a week or so...does MS think customers forget that easily? :) about 481 days ago
  • adrianhara LucasArts is being shut down, but their amazing achievements will live forever...oh and frack Mickey Mouse! about 482 days ago
  • adrianhara argh! can't get anything to show up in @jira's "reviews" tab :( application links are set up, source tab is populated, reviews tab not #cry about 483 days ago

News

Locations of visitors to this page




Adrian Hara Working through the .NET maze

In this post I’ll try to talk a little bit about how you can get msmq work and sql 2005 work to go together in a transactional context. I think it’s fair to say none of the information is new, but I did a bit of digging around the net to find it, so maybe having it all in one place would be of help. Any thoughts or suggestions are more than welcome and I’d be happy to update the post for posterity J So…

…what do you do if you need to:

a)      Receive a message from a MessageQueue

b)      Based on that message, save something to a Sql table

c)       Send a message to (another) MessageQueue

d)      Have all of the above work or fail together (like in…uhm…a transaction ;))

As you might have guessed by now, you need to somehow “wrap” all of the above operations in a transaction, or, let’s say, in the same transactional context. In .net 2.0+, this is deceptively simple:

                  using (TransactionScope transaction = new TransactionScope())

      {

            // do some work

            transaction.Complete();

      }

 

So all you have to do is make sure that all of the operations you need to do (like a,b and c above) are inside the TransactionScope and the work will be transactional. 

 

Now some things to be aware of:

1.       The above will work with MSMQ out of the box, but only with some ado.net database providers, namely those whose implementation looks to see if there is an ambient transaction and enlist in it. The Sql and Oracle providers, at least, support this, so they will work ok using this approach.

2.       You need to have the DTC (Distributed Transaction Coordinator) enabled and configured correctly. I won’t go into details about how to do this, as there is LOTS of information about it on the net. You need it because you have two different transaction types (one for MSMQ and one for the database), whose managers need to work together in order to achieve the “transactional context” goal. They do this by enlisting the help of a “higher power”, namely the DTS.

3.       For your message queues to support transactions, they need to be marked as “Transactional” at creation time. Be careful about this one: I found no obvious way (from the MMC console) to change this after the queue was created AND the behavior of the queue I observed was silent (for example, when sending a message to a transactional queue without using a transaction, the message is not sent, but you don’t get an exception either, so it fails silently).

4.       There’s a class called MessageQueueTransaction. The usage pattern of this class is similar to an Ado transaction, having Begin() and Commit() methods and also requiring that when you do MessageQueue operations, you use overloaded methods which take the transaction as a parameter, like MessageQueue.Send(Message msg, MessageQueueTransaction transaction). BUT, when using a transactional MessageQueue within a TransactionScope, you don’t need this class. What you do is create a MessageQueue normally (like in a transaction-less context) and use it. The only thing you need to do is tell the queue to use transactions. You do this using overloads of Send() and Receive(), like MessageQueu.Send(Message msg, MessageQueueTransactionType transactionType):

            using (TransactionScope transaction = new TransactionScope())

            {

                  Message inputMsg = inputQueue.Receive(MessageQueueTransactionType.Automatic);

                  // do some work

                  transaction.Complete();

            }

5.       Beware of long-running transactions. For example, if your use case is such that you need to receive a message from a queue first, then write something to the DB, you might be tempted to do the following:

 

using (TransactionScope transaction = new TransactionScope())

{

      using (MessageQueue someQueue = new MessageQueue("<queue connection>"))

      {

            Message msg = someQueue.Receive();

            // do something else

      }

      transaction.Complete();

          }

 

This is not ok! It’s wrong because the Receive() method is synchronous and if there’s nothing in the message queue, it will block until there is. This means that your transaction will remain open for that amount of time and will probably time-out, resulting in a MessageQueueException with a message along the lines of “The transaction operations sequence is invalid”.

The correct way to do this is using the BeginPeek() (not BeginReceive(), since this is not designed to work for transactional queues) method, subscribe to the PeekCompleted event of the queue and, in the event handler, use a transaction to actually Receive() any messages from the queue, like so:     

            inputQueue = new MessageQueue(("<queue connection>");

            inputQueue.PeekCompleted += inputQueue_PeekCompleted;

            inputQueue.BeginPeek();

 

            private void inputQueue_PeekCompleted(object sender, PeekCompletedEventArgs e)

            {

                  using (TransactionScope transaction = new TransactionScope())

                  {

                        Message inputMsg = inputQueue.Receive(MessageQueueTransactionType.Automatic);

                        // do some work

                        transaction.Complete();

                  }

                  inputQueue.BeginPeek();

            }

6.       If you only need to do transactional work related to message queues, without any other resources that need to be in the transactional context (like databases), don’t use the TransactionScope, but rather the MessageQueueTransaction class I mentioned earlier. Using this class to control transactions related only to message queues comes with a lesser performance penalty than using a full distributed transaction controlled by DTC.

 

Links and resources:

Using the Microsoft MessageQueue

MSMQ Transactions on CodeProject

MSDN Help on BeginReceive()

Nine Tips to Enterprise –proof MSMQ

 

 

Posted on Wednesday, April 16, 2008 8:02 AM | Back to top


Comments on this post: MSMQ and database transactions

# re: MSMQ and database transactions
Requesting Gravatar...
Thank you so much for this post - I hadn't previously found any documentation pointing out that BeginRecieve was incompatible with DTC COM+ auto-managed transactions - so I've been tearing my hair out trying to work out why it was always committing.
Left by Michael on Jul 31, 2008 4:18 PM

# re: MSMQ and database transactions
Requesting Gravatar...
Hi,

Thanks for pulling this information together in a blog post.

Re: the silent fails in item 3. This is by design. The application needs to enable source journaling if failures are to be tracked.
http://technet.microsoft.com/en-us/library/cc739818(WS.10).aspx
An exception will not be raised as the application cannot be relied upon to be running when the message is sent and delivery attempted, MSMQ message delivery not being synchronous. The application can only receive an exception if there is a problem putting the message in the outgoing queue.

Re: changing queue type. You won't be able to change the transactional property of a queue without recreating it. Being able to change this property at any time would create big problems for MSMQ's internal transaction sequencing/tracking system.

Cheers
John Breakwell (MSFT)
Left by John Breakwell on Jan 18, 2010 7:33 AM

# re: MSMQ and database transactions
Requesting Gravatar...
Thanks for this. I was guilty of committing item #5 and getting the “The transaction operations sequence is invalid” exception.

When using the PeekCompleted event, is there any reason to call or not call the queue.EndPeek?
Left by Chad Lee on Feb 02, 2010 2:16 PM

# re: MSMQ and database transactions
Requesting Gravatar...
Well hmm, in msdn they only say this: "BeginPeek can specify a time-out, which causes the PeekCompleted event to be raised if the time-out occurs before a message appears in the queue. When a time-out occurs without a message arriving in the queue, a subsequent call to EndPeek(IAsyncResult) throws an exception."

...and then in the code example they don't call EndPeek, so my guess is you don't need to call it in the event handler. But it's just a guess :)
Left by Adrian Hara on Feb 02, 2010 3:47 PM

# re: MSMQ and database transactions
Requesting Gravatar...
Excelent information, in my case I have to update a DB when retrieving msmq messages so the approach of using TransactionScope works excellent for me. Thanks a lot!!
Left by Fer Sanchez on Nov 18, 2011 11:34 AM

Your comment:
 (will show your gravatar)
 


Copyright © Adrian Hara | Powered by: GeeksWithBlogs.net | Join free