Special Edition Using Enterprise JavaBeans 2.0

   

Passing the ACID Test

The best way to describe how transactions must be supported by an application is to look at the criteria a transaction must satisfy to be valid. The four major characteristics of a transaction are that it must be atomic , consistent , isolated , and durable . These characteristics are commonly referred to using the mnemonic ACID . If an application's processing of a set of operations can't pass the ACID test, the operations aren't truly being performed as a transaction.

Atomic

The operations within a transaction must be treated as a single, atomic unit. This means that a transaction can only be considered complete and allowed to commit if every operation within it is performed successfully. If a failure occurs at any point, any operations that have been performed must be undone. In short, a transaction must be viewed as an all-or-nothing proposition.

Besides placing a constraint on how a transaction must be processed , the trait of atomicity should also influence how you define a particular transaction's boundaries. A transaction must represent a single unit of work that leaves a system in a valid state after executing in its entirety. If subsets of a transaction are valid on their own, it's possible that the transaction includes more work than it should. A transaction must include all the operations necessary to perform its atomic unit of work, but it shouldn't include any more. In an EJB architecture, the place to enforce this guideline is most often in your session bean methods. You should take the viewpoint that a bean method should implement only a single unit of work. If a high-level operation from the client's point of view corresponds to multiple transactions in your system, you should define individual bean methods for each transaction and then expose another bean method to the client that wraps calls to each of the others.

In the auction example, the AuctionManager session bean must implement an expireAuction method that is called when the scheduled end time for an auction has been reached. The most important task for this method is to stop the auction by placing it into a state in which it no longer accepts bids. If the auction expires without a winner being assigned, stopping the auction by canceling it is all that needs to be done. However, if a valid winning bid has been placed, the auction needs to be closed, a winner assigned, and an e-mail notification sent. The auction is in a valid closed state only when the winner has been assigned and notified, so these three steps must all be executed or the auction cannot be properly closed. Grouping operations such as these into atomic units of work is the way to maintain transactional integrity in the auction system.

Consistent

When a transaction's operations are performed as a whole, they must take the data they manipulate from one consistent state to another. A business application models real-world activities using data to represent aspects of the problem domain that's involved. This model is valid only if the associated data accurately represents the real world at the completion of each transaction. In terms of enforcement, the results of a transaction must not violate any business rules implemented within the objects that are accessed or any referential integrity constraints in the underlying database. Because of the atomic nature of transactions (and the fact that they're isolated, as you'll see next ), inconsistencies after intermediate operations have been performed aren't a problem. What matters is that the integrity of the data must be guaranteed after a transaction has completed.

Transactional consistency can be illustrated with a simple example using a bank account. Suppose you're making a $100 withdrawal from your checking account using an ATM. You insert your card, key your PIN, and select the option to withdraw $100. The system first verifies that you have at least $100 in your account and that the ATM cash dispenser holds at least $100. Given that these conditions are met, the system debits your account, dispenses five $20 bills to you, and prints a receipt of the transaction. Assuming everything works as described, a consistent operation has been performed because the sum of your checking account balance and the cash in your wallet is the same both before and after the transaction. If anything had happened during the process to change that fact, then consistency would have been violated and the transaction would have been invalid. For example, if the cash dispenser reported that it dispensed $100 when it really only gave you $80, you would quickly be pleading your case for a "rollback" at the closest branch office you could find.

Isolated

Companies deploy large-scale distributed systems to support the demands placed on applications by many simultaneous users who require access to shared data. Distributed architectures allow systems to perform at an acceptable pace when accessed by many users, but to be of any benefit, they must isolate concurrent access to the underlying data. In particular, a transaction being executed by one user must be processed in such a way that no other user sees its effects unless (and until) it's committed. Each transaction must appear to be the only transaction being processed. If a concurrent user were exposed to the intermediate results of a transaction that eventually rolled back, any subsequent operations based on that invalid state could compromise the integrity of the system's data. Concurrent transactions must be isolated so that they produce the same results that would occur if they had been run sequentially.

Looking at the auction example, a transaction to submit a bid includes several steps that must be isolated from other transactions. When the submitBid method of EnglishAuctionBean is called, it must make sure that the auction is still open and accepting bids and, if so, that the submitted bid amount is greater than or equal to the required next bid amount. If the bid amount is valid, a new Bid object is created and assigned to be the auction's new leading bid. If other transactions were allowed to operate on the data used by submitBid during this transaction, errors could occur in the system. For example, if another transaction were to close the auction after the transaction to submit the bid checked the auction's status but before it created the new bid, the true winner of the auction would be in question. Also, a simultaneous bid submission could result in a bid lower than the one preceding it being accepted if the two transactions checked the required next bid amount before either of them created a new bid.

Durable

When a transaction commits, any data updates made by that transaction must be durable . This means that the results of a committed transaction must persist regardless of any failure (short of a catastrophe) that occurs after its completion. What this first implies is that when a database or other persistent storage resource is being used, a transaction's updates must be physically written to that resource before the transaction can be reported as having successfully completed. At that point, durability is typically achieved using a failure recovery mechanism. This mechanism might be a backup copy of a database that reflects each update made or a log of permanent data changes that could be used to reconstruct the data in the event of a failure.

In the auction example, a submitted bid accepted by the system must be stored in the database and made durable. If a leading bid were lost and a lower bid were later accepted and declared the winner of the auction, it would violate the business rules of the site and likely destroy any confidence in the system held by the bidders. Backup mechanisms for a database are outside the scope of this book, but by using EJB, the durability requirement of saving updates to the database before declaring a transaction complete is at least satisfied.

Категории