Chapter 1. Introduction to transactions
This chapter introduces transactions by discussing some basic transaction concepts as well as the service qualities that are important in a transaction manager. The information is organized as follows:
- Section 1.1, “What is a transaction?”
- Section 1.2, “ACID properties of a transaction”
- Section 1.3, “About transaction clients”
- Section 1.4, “Descriptions of transaction terms”
- Section 1.5, “Managing transactions that modify multiple resources”
- Section 1.6, “Relationship between transactions and threads”
- Section 1.7, “About transaction service qualities”
1.1. What is a transaction?
The prototype of a transaction is an operation that conceptually consists of a single step (for example, transfer money from account A to account B), but must be implemented as a series of steps. Such operations are vulnerable to system failures because a failure is likely to leave some of the steps unfinished, which leaves the system in an inconsistent state. For example, consider the operation of transferring money from account A to account B. Suppose that the system fails after debiting account A, but before crediting account B. The result is that some money disappears.
To ensure that an operation like this is reliable, implement it as a transaction. A transaction guarantees reliable execution because it is atomic, consistent, isolated, and durable. These properties are referred to as a transaction’s ACID properties.
1.2. ACID properties of a transaction
The ACID properties of a transaction are defined as follows:
- Atomic—a transaction is an all or nothing procedure. Individual updates are assembled and either committed or aborted (rolled back) simultaneously when the transaction completes.
- Consistent—a transaction is a unit of work that takes a system from one consistent state to another consistent state.
- Isolated—while a transaction is executing, its partial results are hidden from other entities.
- Durable—the results of a transaction are persistent even if the system fails immediately after a transaction has been committed.
1.3. About transaction clients
A transaction client is an API or object that enables you to initiate and end transactions. Typically, a transaction client exposes operations that begin, commit, or roll back a transaction.
In a standard JavaEE application, the
javax.transaction.UserTransaction interface exposes the transaction client API. In the context of the Spring Framework, Spring Boot, the
org.springframework.transaction.PlatformTransactionManager interface exposes a transaction client API.
1.4. Descriptions of transaction terms
The following table defines some important transaction terms:
Transaction demarcation refers to starting and ending transactions. Ending transactions means that the work done in the transaction is either committed or rolled back. Demarcation can be explicit, for example, by calling a transaction client API, or implicit, for example, whenever a message is polled from a transactional endpoint. For details, see Chapter 9, Writing a Camel application that uses transactions.
A resource is any component of a computer system that can undergo a persistent or permanent change. In practice, a resource is almost always a database or a service layered over a database, for example, a message service with persistence. Other kinds of resource are conceivable, however. For example, an Automated Teller Machine (ATM) is a kind of resource. After a customer has physically accepted cash from the machine, the transaction cannot be reversed.
A transaction manager is responsible for coordinating transactions across one or more resources. In many cases, a transaction manager is built into a resource. For example, enterprise-level databases typically include a transaction manager that is capable of managing transactions that change content in that database. Transactions that involve more than one resource usually require an external transaction manager.
A transaction context is an object that encapsulates the information needed to keep track of a transaction. The format of a transaction context depends entirely on the relevant transaction manager implementation. At a minimum, the transaction context contains a unique transaction identifier.
A distributed transaction refers to a transaction in a distributed system, where the transaction scope spans multiple network nodes. A basic prerequisite for supporting distributed transactions is a network protocol that supports transmission of transaction contexts in a canonical format. Distributed transactions are outside the scope of Apache Camel transactions. See also: Section 3.2.3, “About distributed transaction managers”.
X/Open XA standard
The X/Open XA standard describes an interface for integrating resources with a transaction manager. To manage a transaction that includes more than one resource, participating resources must support the XA standard. Resources that support the XA standard expose a special object, the XA switch, which enables transaction managers (or transaction processing monitors) to take control of the resource’s transactions. The XA standard supports both the 1-phase commit protocol and the 2-phase commit protocol.
1.5. Managing transactions that modify multiple resources
For transactions that involve a single resource, the transaction manager built into the resource can usually be used. For transactions that involve multiple resources, it is necessary to use an external transaction manager or a transaction processing (TP) monitor. In this case, the resources must be integrated with the transaction manager by registering their XA switches.
There is an important difference between the protocol that is used to commit a transaction that operates on a single-resource system and the protocol that is used to commit a transaction that operates on a multiple-resource systems:
- 1-phase commit—is for single-resource systems. This protocol commits a transaction in a single step.
- 2-phase commit—is for multiple-resource systems. This protocol commits a transaction in two steps.
Including multiple resources in a transaction adds the risk that a system failure might occur after committing the transaction on some, but not all, of the resources. This would leave the system in an inconsistent state. The 2-phase commit protocol is designed to eliminate this risk. It ensures that the system can always be restored to a consistent state after it is restarted.
1.6. Relationship between transactions and threads
To understand transaction processing, it is crucial to appreciate the basic relationship between transactions and threads: transactions are thread-specific. That is, when a transaction is started, it is attached to a specific thread. (Technically, a transaction context object is created and associated with the current thread). From this point until the transaction ends, all of the activity in the thread occurs within this transaction scope. Activity in any other thread does not fall within this transaction’s scope. However, activity in any other thread can fall within the scope of some other transaction.
This relationship between transactions and thread means:
- An application can process multiple transactions simultaneously as long as each transaction is created in a separate thread.
Beware of creating subthreads within a transaction. If you are in the middle of a transaction and you create a new pool of threads, for example, by calling the
threads()Camel DSL command, the new threads are not in the scope of the original transaction.
- Beware of processing steps that implicitly create new threads for the same reason given in the preceding point.
Transaction scopes do not usually extend across route segments. That is, if one route segment ends with
to(JoinEndpoint)and another route segment starts with
from(JoinEndpoint), these route segments typically do not belong to the same transaction. There are exceptions, however.
Some advanced transaction manager implementations give you the freedom to detach and attach transaction contexts to and from threads at will. For example, this makes it possible to move a transaction context from one thread to another thread. In some cases, it is also possible to attach a single transaction context to multiple threads.
1.7. About transaction service qualities
When it comes to choosing the products that implement your transaction system, there is a great variety of database products and transaction managers available, some free of charge and some commercial. All of them have nominal support for transaction processing, but there are considerable variations in the qualities of service supported by these products. This section provides a brief guide to the kind of features that you need to consider when comparing the reliability and sophistication of different transaction products.
1.7.1. Qualities of service provided by resources
The following features determine the quality of service of a resource:
184.108.40.206. Transaction isolation levels
ANSI SQL defines four transaction isolation levels, as follows:
- Transactions are perfectly isolated from each other. That is, nothing that one transaction does can affect any other transaction until the transaction is committed. This isolation level is described as serializable, because the effect is as if all transactions were executed one after the other (although in practice, the resource can often optimize the algorithm, so that some transactions are allowed to proceed simultaneously).
Every time a transaction reads or updates the database, a read or write lock is obtained and held until the end of the transaction. This provides almost perfect isolation. But there is one case where isolation is not perfect. Consider a SQL
SELECTstatement that reads a range of rows by using a
WHEREclause. If another transaction adds a row to this range while the first transaction is running, the first transaction can see this new row, if it repeats the
SELECTcall (a phantom read).
- Write locks are held until the end of a transaction. Read locks are not held until the end of a transaction. Consequently, repeated reads can give different results because updates committed by other transactions become visible to an ongoing transaction.
- Neither read locks nor write locks are held until the end of a transaction. Hence, dirty reads are possible. A dirty ready is when uncommitted changes made by other transactions are visible to an ongoing transaction.
Databases generally do not support all of the different transaction isolation levels. For example, some free databases support only
READ_UNCOMMITTED. Also, some databases implement transaction isolation levels in ways that are subtly different from the ANSI standard. Isolation is a complicated issue that involves trade offs with database performance (for example, see Isolation in Wikipedia).
220.127.116.11. Support for the XA standard
For a resource to participate in a transaction that involves multiple resources, it needs to support the X/Open XA standard. Be sure to check whether the resource’s implementation of the XA standard is subject to any special restrictions. For example, some implementations of the XA standard are restricted to a single database connection, which implies that only one thread at a time can process a transaction that involves that resource.
1.7.2. Qualities of service provided by transaction managers
The following features determine the quality of service of a transaction manager:
18.104.22.168. Support for suspend/resume and attach/detach
Some transaction managers support advanced capabilities for manipulating the associations between a transaction context and application threads, as follows:
- Suspend/resume current transaction—enables you to suspend temporarily the current transaction context, while the application does some non-transactional work in the current thread.
- Attach/detach transaction context—enables you to move a transaction context from one thread to another or to extend a transaction scope to include multiple threads.
22.214.171.124. Support for multiple resources
A key differentiator for transaction managers is the ability to support multiple resources. This normally entails support for the XA standard, where the transaction manager provides a way for resources to register their XA switches.
Strictly speaking, the XA standard is not the only approach you can use to support multiple resources, but it is the most practical one. The alternative typically involves writing tedious (and critical) custom code to implement the algorithms normally provided by an XA switch.
126.96.36.199. Distributed transactions
Some transaction managers have the capability to manage transactions whose scope includes multiple nodes in a distributed system. The transaction context is propagated from node to node by using special protocols such as WS-AtomicTransactions or CORBA OTS.
188.8.131.52. Transaction monitoring
Advanced transaction managers typically provide visual tools to monitor the status of pending transactions. This kind of tool is particularly useful after a system failure, where it can help to identify and resolve transactions that were left in an uncertain state (heuristic exceptions).
184.108.40.206. Recovery from failure
There are significant differences among transaction managers with respect to their robustness in the event of a system failure (crash). The key strategy that transaction managers use is to write data to a persistent log before performing each step of a transaction. In the event of a failure, the data in the log can be used to recover the transaction. Some transaction managers implement this strategy more carefully than others. For example, a high-end transaction manager would typically duplicate the persistent transaction log and allow each of the logs to be stored on separate host machines.