Infinispan can be configured to use and to participate in JTA compliant transactions. Alternatively, if transaction support is disabled, it is equivalent to using autocommit in JDBC calls, where modifications are potentially replicated after every change (if replication is enabled).
On every cache operation Infinispan does the following:
In order to do this, the cache has to be provided with a reference to the environment's TransactionManager. This is usually done by configuring the cache with the class name of an implementation of the TransactionManagerLookup interface. When the cache starts, it will create an instance of this class and invoke its getTransactionManager() method, which returns a reference to the TransactionManager.
Infinispan ships with several transaction manager lookup classes:
- DummyTransactionManagerLookup : This provides with a dummy transaction manager which should only be used for testing. Being a dummy, this is not recommended for production use a it has some severe limitations to do with concurrent transactions and recovery.
- JBossStandaloneJTAManagerLookup : If you're running Infinispan in a standalone environment, this should be your default choice for transaction manager. It's a fully fledged transaction manager based on JBoss Transactions which overcomes all the deficiencies of the dummy transaction manager.
- GenericTransactionManagerLookup : This is a lookup class that locate transaction managers in the most popular Java EE application servers. If no transaction manager can be found, it defaults on the dummy transaction manager.
- JBossTransactionManagerLookup : This lookup class locates the transaction manager running within a JBoss Application Server instance.
Once initialized, the TransactionManager can also be obtained from the Cache itself:
Transactions are being configured at cache level; bellow is a sample configuration:
- transactionManagerLookupClass fully qualified class name of a class that looks up a reference to a javax.transaction.TransactionManager
- syncRollbackPhase (defaults to false) - if true, the cluster-wide rollback phase in two-phase commit (2PC) transactions will be synchronous
- syncCommitPhase (defaults to false) - If true, the cluster-wide commit phase in two-phase commit (2PC) transactions will be synchronous, so Infinispan will wait for responses from all nodes to which the commit was sent
- useEagerLocking (default to false) - when eager locking is set to true, whenever a lock on key is required, cluster-wide locks will be acquired at the same time as local, in-VM locks
For more details on how two phase commit (2PC) is implemented in Infinispan and how locks are being acquired see the section below.
Let's take a simple example to depict how transactions are being implemented in Infinispan and how they interact with the transaction manager.
On (1) the transaction manager creates a new Transaction object and associates it with the current thread.
When (2) takes place, Infinispan acquires a local lock on k1, and associates it with the running transaction. Conceptually, a local lock is different from a cluster lock in the following:
- if the lock is local, same lock (i.e. on same key) can be acquired on another node in the cluster at the same time. E.g. another transaction can successfully acquire a lock on k1, on another node.
- on the same node where the k1 lock was acquired, any other transaction trying to acquire same lock will wait as the lock on k1 is already acquired
- local locks are cheaper, as they don't required RPCs
Within the same call, Infinispan registers an XAResource against the running transaction. This XAResource is called by the TransactionManager on commit (rollback). For more details see step (4). Note that if useEagerLocking is set to true then cluster locks are being acquired at step (2) and (3). This decreases performance as RPC calls are being performed for every cache operation.
On (3), we acquire a local lock is being acquired on k2
- the TransactionManager calls prepare on all registered XAResources. In this case the only registered resource is Infinispan's, at step 2.
- once prepare call is received, Infinispan tries to acquire cluster locks for each acquired local lock. This involves RPC calls to other node is the cluster
- if cluster locks can be acquired for all local locks, then it acknowledges to the TransactionManager that it is prepared(first phase of 2PC) and it is ready to commit the transaction. It is possible that cluster locks cannot be acquired at this step, e.g. if another transaction is in the process of committing and it has a lock on k1.
- if the TransactionManager receives successful acknowledges from all registered XAResources, it calls commit on all resources (or calls rollback, if at least one of them failed to acknowledge the prepare). This is the second phase of the 2PC protocol. At this point Infinispan applies all the changes (it already has the locks) and releases the cluster locks. This also involves RPC calls to all nodes where the data resides. These RPC calls can be performed either synchronously or asynchronously, according to syncCommitPhase or syncRollbackPhase attribute
The fact that Infinispan acquires cluster locks lazily(at commit time) has a significant performance impact, as the number of RPC calls is reduced.
Deadlocks can significantly (up to one order of magnitude, see benchmarks) reduce the throughput of a system, especially when multiple transactions are operating agains the same key set. Deadlock detection is disabled by default, but can be enabled/configured per cache (i.e. under namedCache config element) by adding the following:
Some clues on when to enable deadlock detection. A high number of transaction rolling back due to TimeoutException is an indicator that this functionality might help. TimeoutException might be caused by other causes as well, but deadlocks will always result in this exception being thrown. Generally, when you have a high contention on a set of keys, deadlock detection may help. But the best way is not to guess the performance improvement but to benchmark and monitor it: you can have access to statistics (e.g. number of deadlocks detected) through JMX, as it is exposed via the DeadlockDetectingLockManager MBean. For more details on how deadlock detection works, benchmarks and design details refer to this article.
Note: deadlock detection only runs on an a per cache basis: deadlocks that spread over two or more caches won't be detected.
If a CacheException (or a subclass of it) is thrown by a cache method within the scope of a JTA transaction, then the transaction is automatically marked for rollback.
Transaction recovery is discussed in this document.
By default Infinispan registers itself as a first class participant in distributed transactions through XAResource. There are situations where Infinispan is not required to be a participant in the transaction, but only to be notified by its lifecycle (prepare, complete): e.g. in the case Infinispan is used as a 2nd level cache in Hiberenate.
Starting with 5.0 release, Infinispan allows transaction enlistment through Synchronisation. This can be enabled through the useSynchronization attribute on the transaction element:
Synchronisations have the advantage that they allow TransactionManager to optimize 2PC with a 1PC where only one other resource is enlisted with that transaction (last resource commit optimization). E.g. Hibernate second level cache: if Infinispan registers itself with the TransactionManager as a XAResource than at commit time, the TransactionManager sees two XAResource (cache and database) and does not make this optimization. Having to coordinate between two resources it needs to write the tx log to disk. On the other hand, registering Infinispan as a Synchronisation makes the TransactionManager skip wrtting the log to the disk (performance improvement).