Mark Jen [ex-MSFT]

Indigo/xES Transactions [tx.Rollback() strikes again...]

enlistment anyone?

So, you know you need a transaction and you've created a transaction object. Next, you most likely want to do some transacted work. At this point, there are two possibilities:

1.      you need to use a resource that understands transactions

2.      you need to create a resource that understands transactions

First of all, don’t let the term “resource” scare you; it simply means creating a piece of code that wishes to be informed when a transaction is going through its commit process. Any components you are building that want to know the outcome of a transaction, want to vote during the preparation of the transaction, or want to do durable work in the scope of a transaction all fall into the second category.

For today’s blog, I’ll be focusing on getting started developing a component in the second category. To get stuff rolling, you’ll need to decide whether or not your transacted functionality needs to be durable. Normally, you need to be durable if you have a resource for which you need to protect data integrity. If you don’t have anything that requires you to be able to guarantee data integrity in the face of system failures, then your resource is volatile.

Two main examples of durable resources are databases and file systems. For these resources, data integrity is paramount. Also, things that are built on top of these resources are also typically durable since they are backed by durable storage. In contrast,  a few examples of volatile resources include dialog boxes, diagnostic logging tools and in-memory caches. For the most part, these components are ok if they don’t receive the outcome of the transaction or if they crash and don’t maintain data integrity.

So let’s say you need to protect data integrity. That means you are creating a durable resource and you need to implement an IResourceManager object. This interface gives you a way to expose an identifier to System.Transactions; - it uses this identifier to track entities that require their notifications to be guaranteed. Think of it as a tracking number for your component so that in the event of a crash, the TMs and System.Transactions can make sure you still get notifications for your transactions.

The most basic IResourceManager looks like this:

    class MyResourceManager : IResourceManager

    {

        Guid myGuid;

 

        public MyResourceManager()

        {

            // ... grab myGuid from a durable store (file, databsae, etc) ...

        }

 

        #region IResourceManager Members

        Guid IResourceManager.Identifier

        {

            get { myGuid; }

        }

        #endregion

    }

 

You’ll notice that the identifier Guid doesn’t change. This is very important because if you change your identifier, System.Transactions will think you are a new and different resource and your notifications are no longer guaranteed.

Next, regardless of whether you are creating a volatile or durable resource, you'll need to create an object that implements IEnlistmentNotification. IEnlistmentNotification is an interface that defines how the Transaction Manager (TM) talks to you through System.Transactions. For those that are familiar with the 2 Phase Commit (2PC) protocol, you'll see methods that have familiar names. Here's a sample object that implements IEnlistmentNotificaiton:

    class MyEnlistmentNotification : IEnlistmentNotification

    {

        #region IEnlistmentNotification Members

 

        void IEnlistmentNotification.Prepare(IPreparingEnlistment preparingEnlistment, byte[] recoveryInformation)

        {

            // ... prepare unit of work for durable storage ...

            // ... save recoveryInformation ...

            preparingEnlistment.Prepared();

        }

 

        void IEnlistmentNotification.Commit(IEnlistment enlistment)

        {

            // ... log that unit of work is committed ...

            enlistment.EnlistmentDone();

        }

 

        void IEnlistmentNotification.Rollback(IEnlistment enlistment)

        {

            // ... log that unit of work has rolled back ...

            enlistment.EnlistmentDone();

        }

 

        void IEnlistmentNotification.InDoubt()

        {

            // ... log that unit of work is in doubt

        }

 

        #endregion

    }

What is happening here is you are implementing an interface so that System.Transactions can contact you during each phase in a transaction's commit protocol. This gives you a chance to do whatever work you need to do. When you are finished, you use the callback object passed to you to tell System.Transactions that you are done.

Finally, once you have an object that does what you need it to do, you'll want to enlist it in a transaction. Essentially, what you are doing here is registering your IEnlistmentNotification object with System.Transactions so that it knows to notify you when the transaction is committing. The syntax for doing this is:

    ITransaction tx = Transaction.Create();

    IResourceManager myRM = new MyResourceManager();

    IEnlistmentNotification myEN = new MyEnlistmentNotification();

 

    tx.VolatileEnlist(myEN, false);

    tx.DurableEnlist(myRM, myEN, false);

Confused yet? Let’s review.

·        You have a transaction and you want to do transacted work

·        You need to decide whether the work you are about to do is durable or volatile

·        If your work is durable, you need to create an object implementing IResourceManager

·        Either way, you’ll need an object implementing IEnlistmentNotificaiton

·        Use the DurableEnlist and VolatileEnlist methods on the ITransaction object to inform System.Transactions that you want to receive notifications when a transaction is committing

·        When the transaction commits, your object implementing IEnlistmentNotificaiton will get its corresponding methods called

Ta-da! That’s all there is to it. If you have specific questions or need more detail on a step, please visit the newsgroups and post up your requests.

Before I sign off for today, those of you who are really paying attention will have noticed that the DurableEnlist and VolatileEnlist functions have a Boolean parameter at the end called “enlistDuringPrepareRequired.” You may also be wondering what is in the recoveryInformation byte array passed to you when IEnlistmentNotification.Prepare is called. This info and more will be covered in later blog entries so stay tuned!

Comments

chadbr said:

Good stuff! Keep it coming...
# July 21, 2004 1:35 PM

passby said:

Almost Void blog()

{

return void;

}

# January 22, 2009 9:04 AM