Tuesday, April 26, 2005 9:37 PM
by
malx
Pitfalls with Inherited Transactions
Yesterday I demonstrated how a Transaction can be inherited by
processes. This means that a process never designed for
Transactions can run in a Transacted mode. Clearly, this has
profound implications. Some applications may exhibit odd or
unexpected behavior if run in this way. It isn't really possible
to enumerate every possible case where odd things will happen; the best
approach is to use caution. Let's consider a few cases.
Suppose a non-transactionally aware application recorded a simple log
file. When run from within a transaction, those log records
become isolated from other processes, and can be completely rolled
back, possibly defeating the purpose of the log. Applications
that record log accesses should be aware of this possibility, and may
choose to ensure that they switch out of a Transacted state before
performing logging.
Isolating applications that use temporary files into Transactions has
the side-effect that it becomes virtually impossible to recover data in
event of failure. Suppose you ran Microsoft Word in a
transaction; Word will create a temporary file of your document, which
is isolated from public view. If Word were to fail, the temporary
file remains inaccessible and Word can't be invoked to recover that
lost data in event of a crash. Depending on how Transactions are
managed in this case, the Transaction may even be rolled back, purging
the temporary file forever, and losing data in the process.
The most interesting case, in my opinion, involves launching an
application with incomplete Transaction support from within a
Transaction. The test application in my previous post is a good
example: what happens if test itself is run from within a Transaction?
It will blindly create a second, completely independent transaction, do
some work, and commit that Transaction. Now the application which
called the test application wants to rollback its Transaction - which
it does, successfully - but all of the changes made by the test
application remain committed to disk.
Inheritable Transactions are a powerful user tool, but are also another
case that application developers should be aware of. Well-behaved
applications should firstly check if they are being executed within a
Transaction and make appropriate ajustments. What an application
should do next depends in part upon what the application was intended
to do in the first place. Maybe the application should simply
exit (with an appropriate error message, of course.) Maybe the
semantics of an application could be changed - instead of a Service
Pack installer maintaining its own Transaction, it could instead rely
on its inherited Transaction and not duplicate Transactional
logic. A well behaved application should never silently ignore
the existence of an inherited Transaction; doing this only leads to
confusing, potentially inconsistent results.
The test application does ensure that the child process has completed
before commiting the Transaction. This code is particularly
important. If the test application commits the Transaction while
a child process is still using it, filesystem calls made by the child
will begin to fail. This includes both reads and writes: all
filesystem calls made by the child are made in the context of an
invalid Transaction. The child may not be designed to handle this
case elegantly. When inheriting Transactions, an application
should make sure that no process is still running within that
Transaction before committing or rolling back.
So what happens if the process being executed is intending to remain
executing permanently? Bluntly: don't do this. That process will
eventually exhaust all the resources for recording Transactional
changes, and will invariably fail eventually, possibly failing other
processes as well. If a server is to run with Transaction
support, it should be designed to support Transactions natively.
In each case of using Transactions, the first question to ask yourself
is always: what does this mean? What should I expect? The
implications of Transactions may easily break existing applications, so
use caution doing this. However, Transactional inheritance when
used correctly enables you to leverage this new technology without
heavy reinvestment in new applications.