JTA is supported in versions of Ehcache 2.0 and higher.
Ehcache acts as an XAResouce and participates in JTA ("Java Transaction API") transactions.
Ehcache automatically detects and uses the following transaction managers in the following order:
No configuration is required; they work out of the box.
The first found is used.
If you Transaction Manager is not in the above list or you wish to change the priority you need to configure your own lookup class and specify it in place of the DefaultTransactionManagerLookup in the ehcache.xml config::
<transactionManagerLookup class= "net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup" properties="" propertySeparator=":"/>
You can also provide a different location for the JNDI lookup by providing the jndiName property to the DefaultTransactionManagerLookup.
The example below provides the proper location for the TransactionManager in GlassFish v3:
<transactionManagerLookup class="net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup" properties="jndiName=java:appserver/TransactionManager" propertySeparator=";"/>
JTA is enabled on a cache by cache basis with the transactionalMode cache attribute
The allowed values are xa and off. By default it is off.
Enabling a cache for JTA is shown in the following example:
<cache name="xaCache"
maxElementsInMemory="500"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="1"
transactionalMode="xa">
</cache>Note that in Ehcache 2.0 JTA requires Terracotta serialization value mode otherwise a a CacheException will be thrown on start up. Standalone Ehcache is supported as of Ehcache 2.1.0
If a cache is enabled for JTA all operations on it must happen within a transaction context, otherwise a TransactionRequiredException will be thrown.
The isolation level offered in Ehcache JTA is READ_COMMITTED. Ehcache is an XAResource. Full two-phase commit is supported.
Specifically:
If your XA enabled cache is being used with a writer, write operations will be queued until transaction commit time. Solely a Write-through approach would have its potential XAResource participate in the same transaction. Write-behind, while supported, should probably not be used with an XA transactional Cache, as the operations would never be part of the same transaction. Your writer would also be responsible for obtaining a new transaction...
Using Write-through with a non XA resource would also work, but there is no guarantee the transaction will succeed after the write operation have been executed successfully. On the other hand, any thrown exception during these write operations would cause the transaction to be rolled back by having UserTransaction.commit() throw a RollbackException.
Transactional support is implemented at the Store level, through XaTransactionalStore. The store actually decorates the underlying MemoryStore implementation, augmenting it with transaction isolation and two-phase commit support.
During it's initialization, the Cache will lookup the TransactionManager using the provided TransactionManagerLookup implementation. Using the TransactionManagerLookup.register(XAResouce): void callback, the newly created XAResource is potentially registered with the TransactionManager.
That same TransactionManager will from there on be used by the Cache to access the current transaction on all transactional method calls.
The store is automatically configured to copy every Element read from the cache or written to it. Cache is copy-on-read and copy-on-write.
Every read from the Cache, or remove() without a previous get(), will have the transactional track versioning information for these cache values. This version information will be checked against at commit time, to make sure we're still mutating the same information.
Write operation to the cache (puts and removes) are not effectively executed against the underlying memory store. Rather a local transaction context is being altered. It queues all commands to be executed against the Store at commit time. This context also alters the behavior of cache accesses: e.g. within the same transaction a put and then a getSize call will reflect that previous put, while other transaction would not be impacted. This gives read_commited isolation.
During the two phase commit, the context is first prepared: where all keys to be altered are checked for consistency against the optimistic locking mechanism. If a key to be updated or removed has since been changed by a commited transaction, the transaction will be rolled back. For each key a write lock is acquired, version checked and the old value is copied to a temporary "guarding" store. That store is always queried before the real underlying memory store is. That way, while write locked, old values that are about to be updated can still be read, non blocking, from the "oldVersionStore". If all keys could be validated and write locked the prepare operation as finished successfully and Ehcache votes OK on commit.
If all other XAResource eventually vote OK, the transaction is committed: the old value(s) are removed and write-locks are released. If not, the old values are copied back to the store and the lock released.
As specified by the JTA specification, only prepared transaction data is recoverable. Which means that any transaction still alive at VM failure will be lost. Since all these operations are non locking, the cluster isn't suffering from these.
Prepared data on the other hand, is persisted to the L2 and locks on the memory are being held. Should the L1 come back up before the locks timed out, like network failure, the VM will be able to keep doing its work.
Should the XA recovery scan be required by the transaction manager, Ehcache will provide all Xid prepared, but these will only be able to be rolled back. Indeed when locks time out, no guarantee can be made about the locked keys and their value. If the transaction manager still asks Ehcache to commit these, a HeuristicException will be thrown.
We have three sample applications showing how to use JTA with a variety of technologies.
This sample application uses JBoss application server. It shows an example using User managed transactions. While we expect most people will use JTA from within Spring or EJB where the container rather than managing it themselves, it clearly shows what is going on.
The following snippet from our SimpleTX servlet shows a complete transaction.
Ehcache cache = cacheManager.getEhcache("xaCache");
UserTransaction ut = getUserTransaction();
printLine(servletResponse, "Hello...");
try {
ut.begin();
int index = serviceWithinTx(servletResponse, cache);
printLine(servletResponse, "Bye #" + index);
ut.commit();
} catch(Exception e) {
printLine(servletResponse,
"Caught a " + e.getClass() + "! Rolling Tx back");
if(!printStackTrace) {
PrintWriter s = servletResponse.getWriter();
e.printStackTrace(s);
s.flush();
}
rollbackTransaction(ut);
}The source code for the demo can be checked out from http://svn.terracotta.org/svn/forge/projects/ehcache-jta-sample/trunk
A README.txt explains how to get the JTA Sample app going.
The Idea of this application is to show a real world scenario. AwWeb app reads account transfer messages from a queue and tries to execute these account transfers.
With JTA turned on, failures are rolled back so that the cached account balance is always the same as the true balance summed from the database.
This app is a Spring-based Java web app running in a Jetty container. It has (embedded) the following components:
All XA Resources are managed by Atomikos TransactionManager. Transaction demarcation is done using Spring AOP's @Transactional annotation.
You can run it with: mvn clean jetty:run. Then point your browser at: http://localhost:9080.
To see what happens without XA transactions:
mvn clean jetty:run -Dxa=no
The source code for the demo can be checked out from
{{http://svn.terracotta.org/svn/forge/projects/ehcache-jta-banking/trunk}}
A README.txt explains how to get the JTA Sample app going.
** Examinator
Examinator is our complete application that shows many aspects of caching in one web based Exam application,
all using the Terracotta Server Array.
Check out from {{http://svn.terracotta.org/svn/forge/projects/exam/}}
** Hibernate Transactions
Ehcache is a "transactional" cache for Hibernate purposes. The <<<net.sf.ehcache.hibernate.EhCacheRegionFactory>>>
has support for Hibernate entities configured with <cache usage="transactional"/>.
* Limitations
There is one limitation left in this second release of JTA for Ehcache.
* FAQ
** How do I make WebLogic 10 work with Ehcache JTA?
WebLogic uses an optimization that is not supported by our implementation. By default WebLogic 10 will spawn threads to
start the Transaction on each XAResource in parallel. As we need transaction work to be performed on the same Thread, you will have
to turn this optimization off by setting <<<parallel-xa-enabled>>> option to <<<false>>> in your domain configuration :
jta ... checkpoint-interval-seconds300/checkpoint-interval-seconds parallel-xa-enabledfalse/parallel-xa-enabled unregister-resource-grace-period30/unregister-resource-grace-period ... /jta ---