Modern standards call for a transaction to be atomic, consistent, isolated, and durable. In transaction processing terminology, these properties are referred to as the ACID properties. When you design transactional components, you must adhere to the ACID requirements; they are not optional. As you will see, COM+ enforces them rigorously. Once you understand the ACID requirements and follow simple design guidelines, developing transactional components in COM+ becomes straightforward.
When a transaction completes, all the changes it made to the system’s state must be made as if they were all one atomic operation. The word atom comes from the Greek word atomos, meaning indivisible. The changes made to the system are made as if everything else in the universe stops, the changes are made, and then everything resumes. It is not possible to observe the system with only some of the changes.
A transaction is allowed to change the system state only if all the participating objects and resources execute their part successfully. Changing the system state by making the changes is called committing the transaction. If any object encounters an error executing its part, the transaction aborts and none of the changes is committed. This process is called aborting the transaction. Committing or aborting a transaction must be done as an atomic operation.
A transaction should not leave things to do in the background once it is done, since those operations violate atomicity. Every operation resulting from the transaction must be included in the transaction itself.
Because transactions are atomic, a client application becomes a lot easier to develop. The client does not have to manage partial failure of its request or have complex recovery logic. The client knows that the transaction either succeeded or failed as a whole. In case of failure, the client can choose to issue a new request (start a new transaction) or do something else, such as alert the user. The important thing is that the client does not have to recover the system.
A transaction must leave the system in a consistent state. Note that consistency is different from atomicity. Even if all changes are committed as one atomic operation, the transaction is required to guarantee that all those changes are consistent—that they make sense. The component developer is responsible for making sure the semantics of the operations are consistent. A transaction is required to transfer the system from one consistent state to another. Once a transaction commits, the system is in a new consistent state. In case of error, the transaction should abort and roll back the system from the current inconsistent and intermediate state to the initial consistent state.
Consistency contributes to simple client-side code as well. In case of failure, the client knows that the system is in a consistent state and can use its higher-level logic to decide the next step (or maybe none at all, since the system is in a consistent state).
While a transaction is in progress, it makes changes to the system state. Isolation means no other entity (transactional or not) is able to see the intermediate state of the system. The intermediate state shouldn’t be seen outside of the transaction because it may be inconsistent. Even if it were consistent, the transaction could still abort and the changes could be rolled back. Isolation is crucial to overall system consistency. Suppose Transaction A allows Transaction B access to its intermediate state. Transaction A aborts, and Transaction B decides to commit. The problem is that Transaction B based its execution on a system state that was rolled back, and therefore Transaction B is left unknowingly inconsistent. Managing isolation is not trivial. The resources participating in a transaction must lock the data accessed by the transaction from all others and must synchronize access to that data when the transaction commits or aborts. The transaction monitoring party should detect and resolve deadlocks between transactions using timeouts or queues. A deadlock occurs when two transactions contend for resources the other one holds. COM+ resolves deadlocks between transactions by aborting the deadlocked transactions.
Theoretically, various degrees of transaction isolation are possible. In general, the more isolated the transactions, the more consistent their results are, but the lower the overall application throughput—the application’s ability to process transactions as fast as it can. COM+ 1.0 transactions use the highest degree of isolation, called serialization . This term means that the results obtained from a set of concurrent transactions are identical to the results obtained by running each transaction serially. To achieve serialization, all the resources a transaction in process touches are locked from other transactions. If other transactions try to access those resources, they are blocked and cannot continue executing until the original transaction commits or aborts. The next version of COM+ (see Appendix B) allows configuring the isolation level of your transactions and trades consistency for throughput.
If a transaction succeeds and commits, the changes it makes to the system state should persist in a durable storage, such as a filesystem, magnetic tapes, or optical storage. Transactions require commitment of their changes to a durable storage because at any moment the machine hosting the application could crash and its memory could be erased. If the changes to the system’s state were in-memory changes, they would be lost and the system would be in an inconsistent state. The changes a transaction makes to the system state must persist even if the machine crashes immediately after the decision to commit the changes is made. The component’s developer is required to store the new system state only in durable resources. The durable resource must be robust enough to withstand a crash while trying to commit the changes. One way to achieve such robustness would be to manage log files to recover from the crash and complete the changes.
However, how resilient to catastrophic failure the resource really should be is an open question that depends on the nature and sensitivity of the data, your budget, available time, and available system administration staff. A durable system can be anything from a hard disk to a RAID disk system that has multiple mirror sites in places with no earthquakes.