Section 9.3. Transactions


9.3. Transactions

MSMQ is a WCF transactional resource manager. When you create a queue (either programmatically or administratively), you can create the queue as a transactional queue. If the queue is transactional, then the queue is durable and messages persist to disk. More importantly, posting and removing messages from the queue will always be done under a transaction. If the code that tries to interact with the queue has an ambient transaction, the queue will silently join that transaction. If no ambient transaction is present, MSMQ will start a new transaction for that interaction. It is as if the queue is encased in a transactionScope with transactionScopeOption.Required. Once in a transaction, the queue will commit or roll back along with the accessing transaction. For example, if the accessing transaction posts a message to the queue and then aborts, the queue will reject the message.

9.3.1. Delivery and Playback

When a nontransactional client calls a queued service, client-side failures after the calls will not roll back posting the message to the queue, and the queued call will be dispatched to the service. However, a client calling a queued service may do so under a transaction, as shown in Figure 9-3.

Figure 9-3. Posting to a client-side queue


The client calls are converted to a WCF message (or messages) and then packaged in an MSMQ message (or messages). If the client's transaction commits, these MSMQ messages are posted to the queue and persist there. If the client transaction aborts, the queue discards these MSMQ messages. In effect, WCF provides clients of a queued service with an auto-cancellation mechanism for their asynchronous, potentially disconnected calls. Normal connected asynchronous calls cannot be combined easily, or at all, with transactions, because once the call is dispatched there is no way to recall it in case the original transaction aborts. Unlike connected asynchronous calls, queued services calls are designed for this very transactional scenario. In addition, the client may interact with multiple queued services in the same transaction. Aborting the client transaction due to whatever reason will automatically cancel all calls to those queued services.

9.3.1.1. Delivery transaction

Since the client may not be on same machine as the service, and since the client, the service, or both could be disconnected, MSMQ maintains a client-side queue as well. The client-side queue serves as a "proxy" to the service-side queue. In the case of a remote queued call, the client first posts the message to the client-side queue. When (or if) the client is connected, MSMQ will deliver the queued messages from the client-side queue to the service-side queue, as shown in Figure 9-4.

Figure 9-4. The delivery transaction


Since MSMQ is a resource manager, removing the message from the client-side queue will create a transaction (if indeed the queue is transactional). In case MSMQ fails to deliver the message to the service-side queue, for whatever reason (such as a network fault or service machine crash), the message removal from the client-side queue is rolled back, and the message posting to the service-side queue is canceled, resulting in the message being back at the client-side queue, at which point MSMQ tries again to deliver the message. While you could configure and control the failure handling (as you will see later on), excluding fatal errors that can never be resolved, queued services actually enjoy a guaranteed delivery mechanismif it is technically possible to deliver the message (within the confines of the failure-handling modes), the message will get from the client to the service. In effect, this is WCF's way of providing reliable messaging for queued services. Of course, there is no direct support for the reliable messaging protocol as with connected calls; this is just the analogous mechanism.

9.3.1.2. Playback transaction

Removing the message by WCF from the queue for playback to the service kick-starts a new transaction (assuming the queue is transactional), as shown in Figure 9-5.

Figure 9-5. Playback transaction


The service is usually configured to participate in the playback transaction. If the playback transaction aborts (usually due to service-side exceptions), the message rolls back to the queue, where WCF detects it and dispatches it again to the service. This in effect yields an auto-retry mechanism. Consequently, you should keep the service processing of the queued call relatively short, or risk aborting the playback transaction.

An important observation here is that it is wrong to equate queued calls with lengthy asynchronous calls. With calls dispatched asynchronously, the service is allowed to take its time processing the call, and usually no client-side transaction is involved. In the case of a queued call, the service processing should not time-out the playback transaction, since that will cause WCF to retry the call, because the service has an in-coming transaction.

9.3.2. Service Transaction Configuration

As just demonstrated, in every queued call, assuming transactional queues, there are actually three transactions involved: client, delivery, and playback, as shown in Figure 9-6.

Figure 9-6. Queued calls and transactions


From a design perspective, you rarely, if ever, depict the delivery transaction in your design diagrams and you simply take it for granted. In addition, the service will never participate in the client's transaction, so in effect my four logical transactional modes (Client, Client/Service, Service, None) from Chapter 7 do not apply. If the service contract operation is configured with transactionFlowOption.Allowed and transactionFlowOption.NotAllowed, both of these settings amount to the same resultthe client transaction is never provided to the service. Not only that, but transactionFlowOption.Mandatory is disallowed for configuration on a queued contract and it is verified at the service load time. The real question is the relation between the playback transaction and the service transactional configuration.

9.3.2.1. Participating in playback transaction

From a WCF perspective, the playback transaction is treated as the incoming transaction to the service. To participate in the playback transaction, the service needs to have the operation behavior configured with transactionScopeRequired set to TRue, as shown in Example 9-8 and graphically in Figure 9-5.

Example 9-8. Participating in the playback transaction

 [ServiceContract] interface IMyContract {    [OperationContract(IsOneWay = true)]    void MyMethod( ); } [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] class MyService : IMyContract {    [OperationBehavior(TransactionScopeRequired = true)]    public void MyMethod( )    {       Transaction transaction = Transaction.Current;       Debug.Assert(transaction.TransactionInformation.                    DistributedIdentifier != Guid.Empty);    } } 

An interesting point made in Example 9-8 is that both with MSMQ 3.0 and MSMQ 4.0, every transaction always uses the DTC for transaction management, even in the case of a single service and a single playback.

9.3.2.2. Ignoring the playback transaction

If the service is configured for not having any transactions (as in Example 9-9 and graphically in Figure 9-7), WCF will still use a transaction to read the message from the queue, except that transaction is always going to succeed (barring an unforeseen failure in MSMQ itself). Exceptions and failures at the service itself will not abort the playback transaction.

Example 9-9. Ignoring the playback transaction

 [ServiceContract] interface IMyContract {    [OperationContract(IsOneWay = true)]    void MyMethod( ); } [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] class MyService : IMyContract {    public void MyMethod( )    {       Transaction transaction = Transaction.Current;       Debug.Assert(transaction == null);    } } 

Figure 9-7. Ignoring the playback transaction


Services that do not participate in the playback transaction will not have an automated retry by WCF in the case of a playback failure, and the played-back call could fail while the de-queued transaction commits. The main motivation for configuring services to let go of the benefit of auto-retry is lengthy processing, because by not participating in the playback transaction, the call could take any amount of time to complete.

9.3.2.3. Using a separate transaction

You can also write the service so that it manually uses a new transaction, as shown in Example 9-10 and graphically in Figure 9-8.

Example 9-10. Using a new transaction

 [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] class MyService : IMyContract {    public void MyMethod( )    {       using(TransactionScope scope =                           new TransactionScope(TransactionScopeOption.RequiresNew))       {          ...          scope.Complete( );       }    } } 

Figure 9-8. Using a new transaction


When the service uses its own new transaction, it should also prevent using the playback transaction (by defaulting to the transactionScopeRequired value of false) so as not to affect the playback transaction in any way. This of course negates the benefit of the auto-retry mechanism. By having a new transaction separate from the playback transaction, the service is given the opportunity to perform its own transactional work. You would typically configure a service to use its own transaction when the queued operation being called is nice to have and should be performed under the protection of a transaction, yet not retried in case of a failure.

9.3.3. Nondurable Queues

The MSMQ queues described so far were both durable and transactional. The messages persisted to the disk, and posting and reading a message out of the queue was transactional. MSMQ also supports nontransactional volatile queues. Such queues are stored in memory and do not use transactions at all. Because messages are stored in memory, if the machine shuts down or crashes, all messages in the queue are lost. When you create a queue (either using the MSMQ administration tool or programmatically), you can configure it to be transactional or not, and that selection is fixed for the life of the queue. Nondurable queues do not offer any of the benefits of transactional messaging systems such as auto cancellation, guaranteed delivery, and auto-retries. As unadvisable as it is, WCF can work with nondurable queues. MsmqBindingBase (the base class of NetMsmqBinding) offers the Boolean Durable property, which defaults to true.

 public abstract class MsmqBindingBase : Binding,... {    public bool Durable    {get;set;}    public bool ExactlyOnce    {get;set;}    //More members } public class NetMsmqBinding : MsmqBindingBase {...} 

When set to false, WCF will not use transactions for accessing the queue. Both the client and the service must use the same value for the Durable property. The queue itself should be configured to be volatile. When using a volatile queue, if the client transaction aborts, the message or messages will stay in the queue and be delivered to the service. If the playback transaction aborts, the message is lost.

Because of the lack of guaranteed delivery, when using a volatile queue WCF also requires that you set the ExactlyOnce property of the binding to false (the default is true); otherwise, WCF will throw an InvalidOperationException at the service load time. Consequently, here is a consistent configuration for a nondurable queue:

 <netMsmqBinding>    <binding name ="VolatileQueue" durable = "false" exactlyOnce = "false">    </binding> </netMsmqBinding> 




Programming WCF Services
Programming WCF Services
ISBN: 0596526997
EAN: 2147483647
Year: 2004
Pages: 148
Authors: Juval Lowy

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net