What is this page about?
This page is about aligning JBossCache and Hibernate better in ideological terms, with a goal to improve the integration of and interoperability between these two products and make JBossCache the default caching provider in Hibernate.
Details of discussions about this subject are on this forum thread
Issues at Hand
1. Hibernate's caching needs involve caching 4 separate types of objects: entities, collections, queries and update-timestamps (related to queries), each requiring different caching characteristics.
2. When dealing with entities, JBossCache has a concept of a put(), which puts data into the cache. Hibernate, on the other hand, has 2 separate notions of a put() - one is where data is READ from the database and put into the cache, and the other is where data is WRITTEN to the database and put into the cache. The two cases drive separate requirements and behavioural patterns for locking, etc.
3. When dealing with entities, the way Hibernate organises data in the tree structure of JBossCache leads to inefficiencies and contention with concurrent threads.
4. Replicating user types can cause issues when the classloader that loads JBoss Cache does not have visibility to the types being replicated. See this forum thread for details.
5. JBoss Cache and Hibernate both make use of the JTA Synchronization interface, with an implicit assumption that the Hibernate synchronization will be called before the JBC one. This is an invalid assumption, as the JTA spec makes no guarantee of ordering between synchronizations. The fact that this usually works is due to an implementation detail of various transaction managers. We have found that with JBossTS it often fails. See this forum thread for further discussion.
6. JBoss Cache 1.x and 2.x are API incompatible. We need to develop a strategy for dealing in the Hibernate code base with the existence of two incompatible JBC flavors.
7. Using JBossCache as second level cache and optimistic locking, rollbacks can still happen in systems under load as concurrent transactions could be modifying data in the cache. This is a currently pushed back to the Hibernate clients. HHH-2340
Based on some of the ideas discussed on the forum threads above, as well as phone and face-to-face discussions between JBossCache and Hibernate folk, we have a few steps that can be taken to overcome the issues at hand.
1. The solution for this is:
In the short term, for Hibernate to provide a TreeCacheProvider that creates 3 JBossCache instances, one for each cache type. The JBossCache instances should be configured using the multiplexer to significantly reduce the "weight" of multiple JBoss Cache instances.
In the long term, to define the different caches explicitly within Hibernate.
2. The solution here is to create a putForExternalRead() method in JBossCache, which Hibernate could call when doing a database READ. The characteristics of such a method would be:
Suspend all ongoing TXs.
If using pessimistic locking, use weaker locking semantics (READ_UNCOMMITTED? - needs further discussion)
Quieter logging. Errors and the like that would otherwise raise alarm bells should be suppressed since this is a non-critical call.
Run this call in a separate thread, so it does not block (? Needs discussion too - should this be the responsibility of the TreeCacheProvider in Hibernate?)
3. Consider the use of hash buckets under the 'type' node so that contention is reduced?
4. The Hibernate Cache implementation(s) for JBC should make use of the JBC region-based marshalling feature.
5. No clear solution at this point.
6. Different providers? We currently have TreeCacheProvider and OptimisticTreeCacheProvider. How about a org.hibernate.cache.jbosscache200.PessimisticCacheProvider and org.hibernate.cache.jbosscache200.OptimisticCacheProvider ??
7. Hibernate should either retry or JBossCache should fail silently on this. As long as the data is stored in the db correctly, failure putting the data in the cache could fail silently. Next time someone requests the entity, it'd be retrieved from db and put in the cache.
There are currently a bunch of EJB3 clustered entity test failures in AS trunk due to a bad interaction between Hibernate 3.2.4.SP1 and JBoss Cache 2.0.0. Basically, this is a temporary mismatch between the Hibernate and JBC releases currently integrated in AS trunk. There's work in Hibernate Core's trunk for the 3.3 release that can let us resolve this, but for now the tests are going to fail.
Problem is the integration layer ends up calling the above discussed JBC putForExternalRead() method when it needs to cache something in the "UpdateTimestampsCache". JBC treats a call to this method as a no-op if the cache node in question already exists. This is new behavior in JBC 2.0. It's the necessary and intended behavior when the method is called for caching entities and collections. But when it's called for the UpdateTimestampsCache, it causes problems. The Hibernate 3.3. codebase gives us the flexibility to call a different method for the UpdateTimestampsCache call; to fix the problem we need determine the exact behavior we want for the UpdateTimestampsCache call and make the appropriate call to JBC.
The JBC and Hibernate teams will be meeting the week of July 23rd to sort through the various issues on discussed on this page; the UpdateTimestampsCache issue will be one of the items we'll resolve.