As our team's primary goal is to promote ubiquity of transaction use, some of us have spent quite a bit of time thinking about the problems associated both with adding in new types of transacted resources, especially transacted variants of currently non-transacted resources, and in continuing to deal with a world where only part of your data is transaction aware, especially as it becomes the predominant part.
The answer that everyone would like to have is that we could wave a magic wand and suddenly non-transacted resources would just simply be transacted. That I could just write:
atomic
{
DoFirstThing ();
DoSecondThing ();
}
And somehow everything done under DoFirstThing and DoSecondThing would be incorporated into the overall transaction -- even if DoFirstThing and DoSecondThing already exist and were written for a non-transacted world
For transaction-aware code written to System.Transactions, COM+, or Indigo, that is/will be effectively true. But what about the objects and resources used in those, or other, routines that aren't transaction-aware? Could we just make them transaction-aware? This is what I call “injecting transactedness”.
It's a really seductive idea. In the general case, though, it doesn't work.
The key is that by injecting transactedness the caller has changed the contract that the internal objects and resources have with DoFirstThing and DoSecondThing.
For instance, let's say that DoFirstThing does the following (in pseudo-code):
void DoFirstThing (string key, string val)
{
hashTable.Add (key, val);
try
{
using (TransactionScope s = new TransactionScope ())
{
... get sql connection set up ...
... add {key, val} entry to sql table ...
s.Complete ();
}
catch (Exception e)
{
if (!hashTable.ContainsKey (key))
... invariant check failed, terminate process ...
hashTable.Remove (key);
}
}
While this is a purposefully simplified pseudo-code example, here we have a case where if the transaction asynchronously aborts (and System.Transactions does do eager abort), this working code will suddenly fail an internal validity check and failfast.
There are other cases that are very easy to imagine, such as activity logs that are meant to record all activity, even that which eventually fails, or trace data. There are more complex cases, as well:
- many involve what happens at transaction abort, or
- others involve multithreading and intended data leakage, or
- others involve transaction-aware routines that always intended to be the roots of their transactions.
All are examples of what happens when you change the semantic contract of another software component from the outside.
Posted
Apr 27 2005, 09:25 PM
by
jim-johnson