Transactions


A transaction is a series of interactions with a system service that the service combines into one unit of work. A transaction has a begin point and end point. While the transaction is active, the interactions in the transaction are bound together by the four ACID properties of transactions: atomicity, consistency, isolation, and durability. The application starts by asking the service to begin a transaction. After the transaction begins, the application may issue multiple requests to the service, to fetch, insert, change, or delete the information that the service manages. During this time, the application is isolated from the activities of other concurrent transactions. These other transactions may be viewing or changing the same information, but the interactions between the transactions are controlled and predictable. To end the transaction, the application requests that the service either accept (commit) all of the changes or discard (roll back) all of the changes. Either way, it is all or nothing. In other words, the service commits or rolls back the changes as one atomic unit of work. The service commits the changes only if the changes are consistent with any constraints on the information that the service is charged with enforcing. A committed transaction is durable, that is to say, the committed changes will not be lost as a result of service interruptions.

A transaction is local when it is limited to one service. It is distributed when it allows more than one service to participate in the transaction.

Transactions simplify the application's use of the service. If the service says that it cannot commit, then the application knows it can roll back the transaction, and thereby discard the changes that occurred since the transaction began. If the service reports that it successfully committed the transaction, then the application knows that all is well.

By supporting transactions, JDO provides an essential requirement for most application designs. For example, to transfer money from one account to another, a banking application debits one account and credits the other. By making both the debit and the credit actions part of one transaction, the banking application ensures that money is neither created nor destroyed. When the transaction commits successfully, both the debit and the credit actions are successful. When the transaction is rolled back, neither the credit nor the debit action has any effect. By using a transaction, the application can guarantee that debits and credits are paired.

Although the details of transactions are outside the scope of this book, the general concepts are familiar to most application programmers who have coded to a database service.

Optimistic and Datastore Transactions in JDO

JDO defines both datastore transactions and optimistic transactions. This book uses the term database transaction to refer to the transaction provided by the datastore service and to distinguish it from a JDO datastore transaction. As a general rule, it is necessary to keep a connection to the database open in order to keep the database transaction open.

The persistence manager may perform its actions transactionally when either an optimistic or datastore transaction is active, or it may perform its actions nontransactionally when a transaction is not active. So far as possible, each persistence manager uses no more than one database connection at a time.

JDO's datastore transaction relies directly on the transactional service of the datastore. During the entire time that the datastore transaction is active, there is one corresponding database transaction that the persistence manager uses to load and store persistent state. For this reason, during the time that the datastore transaction is active, the persistence manager very likely has an open connection to the database.

On the other hand, to support one optimistic transaction, the typical JDO implementation uses a concurrency value and one or more database transactions. After JDO reads field values from the datastore as a result of queries or transparent persistence, JDO may release the datastore connection. As a result, when n optimistic transactions are active, JDO may be using less than n datastore connections. At the same time, the use of optimistic transactions can reduce the number of database locks that JDO acquires. (The next section has a brief discussion of database locks.)

When the optimistic transaction commits, JDO uses a database transaction to verify the concurrency value and update the datastore. During verification, the concurrency values of all transactional application data objects are checked against the values in the datastore. When the concurrency values of transactional objects are up-to-date, JDO writes the state of modified transactional objects to the datastore. After the database transaction commits, the optimistic transaction ends.

In JDO 1.0, when the commit method fails because of concurrency verification, it throws a JDOUserException. The exception contains an array of JDOUserException objects, one for each object that failed verification. Although it is possible in JDO 1.0 to recover in some deployment environments from a concurrency failure, in the case of EJBs with container-managed transactions, recovery is not possible.

Starting with the 1.0.1 specification, when the commit method fails because of concurrency verification, it throws a JDOOptimisticVerificationException. The exception contains an array of JDOOptimisticVerificationException objects, one for each object that failed verification. When the commit method throws a JDOOptimisticVerificationException, the commit has failed and the transaction has rolled back. The application does not have the option to recover from a JDOOptimisticVerificationException. Chapter 7 describes the exception types defined by JDO.

Note

It is up to the JDO implementation how it calculates the optimistic concurrency value. JDO requires that the implementation fail at least one transaction when two of its persistence managers have concurrently active transactions in which incompatible changes occurred on objects with the same identity value.

By calling the makeTransactional method in the PersistenceManager, the application can force a check of the application data object's concurrency value even when the application does not modify the object.

Isolation Levels and Interactions Between Transactions

The isolation level is one of the four ACID properties of transactions. Because isolation can impose a high performance penalty on the database, most databases offer more than one isolation level. At all levels of isolation, an active transaction can read any changes committed by transactions that ended before it started. (This discussion ignores the access control provided by the database.) Likewise, an active transaction can read its own insertions and modifications, whereas it cannot read its own deletions.

Four isolation levels are generally recognized, and they are defined in terms of interactions that they allow to occur between concurrent transactions. Each level is more stringent than its preceding level.

  • Read-uncommitted: The transaction can see the effects of modifications, insertions, and deletions made by other active (and therefore not yet committed) transactions. That is to say, information that the other unfinished transaction deleted is not visible, while its insertions and modifications are visible. Reading an uncommitted modification made by another transaction is called a dirty read. Nonrepeatable reads and phantom reads, described in the next bullet item, can also occur. Because read uncommitted provides very little isolation, most databases support only read operations in transactions that use the read-uncommitted isolation level.

  • Read-committed: Dirty reads do not occur. The transaction may encounter nonrepeatable reads and phantom reads. A nonrepeatable read occurs when rereading the same state shows a change due to a committed modification made by another transaction. A phantom read occurs when the same query executes twice in a transaction and the two sets of query results do not match up because of the actions of a second committed transaction. Either the second query finds results that the first query did not find, or it does not find results that first query found.

  • Repeatable-read: Dirty reads and nonrepeatable reads do not occur. On the other hand, the transaction can still encounter phantom reads.

  • Serializable: Dirty reads, nonrepeatable reads, and phantom reads do not occur. The transaction never sees the modifications, insertions, or deletions made by other transactions that commit after it started.

High isolation is always in direct conflict with high performance.

Dirty reads, nonrepeatable reads, and phantom reads are well-known transactional abnormalities, but the list of possible abnormalities is longer. Two that are particularly interesting to look at when using JDO are outdated updates and inconsistent reads. An outdated update occurs when one transaction reads information, but before this transaction modifies the information and commits its changes, another transaction changes the same information and commits its changes. If the first transaction can then modify the information and commit, it has used information that is not up-to-date when its transaction commits.

An inconsistent read occurs when one transaction starts to read a series of values, but before it completes the series, another transaction modifies two values in the series, one that the first transaction has read and one that it has not. The second transaction then commits its changes before the first transaction completes its series of reads. If the second transaction can commit its changes and the first transaction then reads one of the modifications made by the second transaction, the series of values that the first transaction read is not consistent with any snapshot of committed state in the datastore.

The isolation level determines whether inconsistent reads and outdated updates can occur in the transaction and if so, whether the transaction can commit successfully. The read-committed isolation level allows both inconsistent reads and outdated updates. The serializable isolation level prevents both.

Some databases allow the application to lock the value for updating when it reads it. For example, Oracle permits the SELECT-FOR-UPDATE syntax. By using locks, the application effectively upgrades the isolation level. In the case of Oracle, update locks can prevent nonrepeatable reads, outdated updates, and inconsistent reads even when the isolation level is read-committed. As a result of the locking, one of the conflicting transactions will wait for the other to end, thereby allowing both transactions to be successful without the transactional abnormality occurring. Because locks cause waiting, they allow deadlock to occur. Deadlock arises when two transactions each hold a lock that the other one needs to proceed. Most databases roll back deadlocked transactions after a period of time.

The Implementation Defines the Isolation Level of Its Transactions

The JDO implementation defines the isolation level that its transactions support. By selecting an isolation level and using the locks that the datastore supports, JDO implementations usually prevent nonrepeatable reads, outdated updates, and inconsistent reads in datastore transactions. For this reason, datastore transactions are sometimes called pessimistic transactions, since they are designed to prevent rather than fail most transactional abnormalities. Unless the JDO implementation uses the serializable isolation level in the datastore, phantom reads can still occur.

JDO requires that optimistic transactions fail during commit when an outdated update occurs. On the other hand, JDO leaves it up to the application to decide whether it wants to fail on commit when a nonrepeatable read and inconsistent read occur. The application forces JDO to fail when these transactional abnormalities occur by making transactional the objects that it reads but does not modify. To avoid a performance penalty, applications should use this tool sparingly. See the makeTransactional method in the PersistenceManager interface, which Chapter 3 describes. Phantom reads can always occur in an optimistic transaction.




Using and Understanding Java Data Objects
Using and Understanding Java Data Objects
ISBN: 1590590430
EAN: 2147483647
Year: 2005
Pages: 156
Authors: David Ezzio

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net