I'll start with the simplest transaction variety, the tightly coupled single-resource transaction. Because all single-resource transactions are tightly coupled, I'll simplify the terminology by calling them
. Let me take you through a typical single-resource transaction involving a database.
Consider writing an application to withdraw money from a checking account. Let's say that "
money from a checking account" means the following:
Finding the specific account record in the CheckingAccount table.
Reading the CurrentBalance field.
Deducting the amount from the current balance.
Logging the withdrawal in a Logging table.
Imagine the problems that would be caused by any of the following events:
We read the balance from the account record, but before we have a chance to deduct the withdrawal amount, the account is closed by another program.
We deduct the amount from the current balance, but we are unable to log the withdrawal in the Logging table.
We log the withdrawal, but we are unable to deduct the amount.
This is just a sample of the things that can go wrong. When all of these updates are heading toward a single
aware resource (i.e., a database), the resource itself can prevent any of these scenarios.
With a transactionally aware resource, we can enclose all of these withdrawal database activities inside a single transaction. We can then ask the database to process that transaction as a whole. The database will then process either all or none of the transaction updates. The database gets to choose which. Further, the database
that if it does process the entire transaction, it will do so without consistency errors (such as the account being closed after the balance has been read) and without any chance of losing the updates after the fact (say, because the disk drive
to choose that inopportune moment to disintegrate).
How the database makes these guarantees is a secret known only to the database. We don't care. We care only about deciding which updates to bunch together into the transaction and about letting the database know where the transaction boundary begins and ends.
Several techniques are available for letting the database know about the transaction boundaries, but most use some transaction boundary-marking APIs, such as BeginTransaction and Commit (the standard
for EndTransaction). Any database updates issued after BeginTransaction and before EndTransaction are assumed to be part of the same transaction collection. The checking account system then looks something like this:
Issue a BeginTransaction request.
Issue a Read request to get the CurrentBalance field from the appropriate account record.
Do whatever programming is necessary to update the account balance.
Issue an Update request to the database to store the new account balance.
Issue an Append request to the database to log the withdrawal in the Logging table.
Issue a Commit request, signaling to the database that you have reached the end of the transaction.
Once the database
that the transaction has been concluded, it will attempt to apply all of the Update and Append
en masse. If, by some horrible chance, the database concludes that one or more of the updates or appends is
, it will toss out the whole collection and notify you, giving you time to drown your sorrows at the local pub or take other appropriate remedial action.
Figure 3.1 illustrates the flow in a tightly coupled single-resource transaction.
Figure 3.1. Flow in a Tightly Coupled Single-Resource Transaction