JBoss Community Archive (Read Only)

Infinispan 6.0

Infinispan transactions

JTA Support

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:

  1. Retrieves the current Transaction associated with the thread
  2. If not already done, registers XAResource with the transaction manager to be notified when a transaction commits or is rolled back.

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:

//the cache must have a transactionManagerLookupClass defined
Cache cache = cacheManager.getCache();

//equivalent with calling TransactionManagerLookup.getTransactionManager();
TransactionManager tm = cache.getAdvancedCache().getTransactionManager();

Configuring transactions

Transactions are being configured at cache level; bellow is a sample configuration:

<transaction 
      transactionManagerLookupClass="org.infinispan.transaction.lookup.GenericTransactionManagerLookup"
      transactionMode="TRANSACTIONAL"
      lockingMode="OPTIMISTIC"/>
  • transactionManagerLookupClass fully qualified class name of a class that looks up a reference to a javax.transaction.TransactionManager

  • transactionMode - configures whether the cache is transactional or not

  • lockingMode - configures whether the cache uses optimistic or pessimistic locking.

For more details on how two phase commit (2PC) is implemented in Infinispan and how locks are being acquired see the section below. All possible transactional settings are available in Configuration reference

Transactional modes

Starting with Infinispan 5.1 release a cache can accessed either transactionally or non-transactionally. The mixed access mode is no longer supported. There are several reasons for going this path, but one of them most important is a cleaner semantic on how concurrency is managed between multiple requestors for the same cache entry.

By default, all Infinispan caches are non-transactional. A cache can be made transactional by changing the transactionMode attribute:

<namedCache name="transactional">
  <transaction transactionMode="TRANSACTIONAL"/>
</namedCache>

transactionMode can only take two values: TRANSACTIONAL and NON_TRANSACTIONAL; one can configure a transactional cache programatically as well:

Configuration c = new ConfigurationBuilder().transaction().transactionMode(TransactionMode.TRANSACTIONAL).build();
assert c.transaction().transactionalCache();

Do not forget to configure a TransactionManagerLookup for transactional caches.

Supported transaction models are optimistic and pessimistic. Optimistic model is an improvement over the old transaction model as it completely defers lock acquisition to transaction prepare time. New approach reduces lock acquisition duration and increases throughput which in turn avoids deadlocks significantly. In pessimistic model, cluster wide-locks are acquired on each write operation only being released after the transaction completed.

Optimistic Transactions

With optimistic transactions locks are being acquired at transaction prepare time and are only being held up to the point the transaction commits (or rollbacks). This is different from the 5.0 default locking model where local locks are being acquire on writes and cluster locks are being acquired during prepare time.

Optimistic transactions can be enabled in the configuration file:

<namedCache name="transactional">
  <transaction transactionMode="TRANSACTIONAL" lockingMode="OPTIMISTIC"/>
</namedCache>

or programatically:

Configuration c = new ConfigurationBuilder().transaction().lockingMode(LockingMode.OPTIMISTIC).build();
assert c.transaction().lockingMode() == LockingMode.OPTIMISTIC;

By default, a transactional cache is optimistic.

Pessimistic Transactions

From a lock acquisition perspective, pessimistic transactions obtain locks on keys at the time the key is written. E.g.

transactionManager.begin();
cache.put(k1,v1); //k1 is locked
cache.remove(k2); //k2 is locked when this returns
transactionManager.commit();

When cache.put(k1,v1) returns k1 is locked and no other transaction running anywhere in the cluster can write to it. Reading k1 is still possible. The lock on k1 is released when the transaction completes (commits or rollbacks).

Pessimistic transactions can be enabled in the configuration file:

<namedCache name="transactional"/>
  <transaction transactionMode="TRANSACTIONAL" lockingMode="PESSIMISTIC"/>
</namedCache>

or programatically:

Configuration c = new ConfigurationBuilder().transaction().lockingMode(LockingMode.PESSIMISTIC).build();
assert c.transaction().lockingMode() == LockingMode.PESSIMISTIC;

Backward compatibility

The autoCommit attribute was added in order to assure backward compatibility. If a cache is transactional and autoCommit is enabled (defaults to true) then any call that is performed outside of a transaction's scope is transparently wrapped within a transaction. In other words Infinispan adds the logic for starting a transaction before the call and committing it after the call.

Therefore if your code accesses a cache both transactionally and non-transactionally all you have to do when migrating to Infinispan 5.1 is mark the cache as transactional and enable autoCommit (that's actually enabled by default)

The autoCommit feature can be managed through configuration:

<namedCache name="transactional">;
  <transaction transactionMode="TRANSACTIONAL" autoCommit="true"/>
</namedCache>

What do I need - pessimistic or optimistic transactions?

From a use case perspective, optimistic transactions should be used when there is not a lot of contention between multiple transactions running at the same time. That is because the optimistic transactions rollback if data has changed between the time it was read and the time it was committed (writeSkewCheck).

On the other hand, pessimistic transactions might be a better fit when there is high contention on the keys and transaction rollbacks are less desirable. Pessimistic transactions are more costly by their nature: each write operation potentially involves a RPC for lock acquisition.

Deadlock detection

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:

<deadlockDetection enabled="true" spinDuration="1000"/>
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.

Transaction and exceptions

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 on node failures

Transaction recovery is discussed in this document.

Enlisting Synchronization

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:

<transaction useSynchronization="true"/>
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).

JBoss.org Content Archive (Read Only), exported from JBoss Community Documentation Editor at 2020-03-11 09:38:51 UTC, last content change 2012-01-23 20:26:11 UTC.