Delaying action at transaction commit

I am designing a back-end where Database contention is the number-one issue for scalability.

We use those darn distributed transactions and therefore I do not know how long the transaction my component is participating into will last.  If a transaction remains open for a few seconds, many of those would create contention in the Database my component is using.  This will result in dead-locks, time-outs and scalability would go out of the window.

I didn’t want to go the all no-sql way and forgo any transaction management.  The crux of the deal is that during one of those distributed transaction, we are inserting one record while a recurrent task performs a large and complicated select on the entire table.  I though if I could wait long enough to insert, until the end of the transaction, I would reduce the contention.  Here is how to do it.

First, what you do not want to do is to hook on the TransactionCompleted event of the Transaction.Current object.  It is tempting, it is easy, it is right at hand, but it doesn’t work.  Well…  it works but whatever code you run in the event handler will exist outside the transaction since as the name of the event suggests, the transaction has already completed by then.  The point of using transaction is to have many actions being atomically bundled so I didn’t want to have a key operation happening outside the transaction.

The real solution isn’t that much more complicated actually.  Check out Transaction.Current.EnlistVolatile.  This method allow you to hook yourself in the transaction process.  You basically become a transaction participant.  We use the volatile version of the method since we do not want to appear as a durable participant since that would involve us being able, like the antic Pheonix, to be born again from our ashes if the process fail.  No need for that, I was happy for this to work only if the original process stayed online for the duration of the transaction.

public Enlistment EnlistVolatile( IEnlistmentNotification enlistmentNotification, EnlistmentOptions enlistmentOptions )

We need to implement the IEnlistmentNotification interface:

public interface IEnlistmentNotification
        void Commit(Enlistment enlistment);
        void InDoubt(Enlistment enlistment);
        void Prepare(PreparingEnlistment preparingEnlistment);
        void Rollback(Enlistment enlistment);

In my case I simply needed to hold a few variable within the object implementing that interface.  Basically the data I wanted to insert in the DB and the Transaction instance itself. Then I had the simple following implementation:

That’s it!  You’re now in the Transaction pipeline and can do late operations.

2 responses

  1. davidlacerte 2012-09-30 at 17:23

    Hey Vincent, That’s interesting, what I don’t see though (and I didn’t go and see for myself, it’s easier to simply ask you ;-)) is what in that tells me that this volatile is called at the end of the transaction? I understand that you want to do the insert at the end, so that you don’t lock the table for the time of the long, read-only select operation (I assume you don’t lock the table while reading from it). But then, what tells me that the volatile isn’t in fact called at the beginning of the transaction, or in the middle of it?

  2. Vincent-Philippe Lauzon 2012-10-10 at 09:05

    Hum… well, I tried it and it’s at the end? But also the contract of IEnlistmentNotification stipulates when the methods are called.

Leave a comment