Following some of the principles set out by Brian Stansberry in Reference Manual: JBoss Cache 3 as a Hibernate 3.5 Second Level Cache and taking in account improvements introduced by Infinispan, an Infinispan JPA/Hibernate second level cache provider has just been developed. This wiki will explain how to configure JPA/Hibernate to use the Infinispan and for those keen on lower level details, the key design decisions made and differences with previous JBoss Cache based cache providers.
If you're unfamiliar with JPA/Hibernate Secone Level Caching, I'd suggest you have a read to Chapter 2.1 in this guide which explains the different types of data that can be cached.
1. First of all, to enable JPA/Hibernate second level cache with query result caching enabled (Note: Query result caching, or for that matter entity caching, may not improve application performance. Be sure to benchmark your application with and without caching.), add either of the following:
<!-- If using to JPA, add to your persistence.xml --> <property name="hibernate.cache.use_second_level_cache" value="true"/> <property name="hibernate.cache.use_query_cache" value="true"/>
<!-- If using to Hibernate, add to your hibernate.cfg.xml --> <property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.use_query_cache">true</property>
2. Now, configure the Infinispan cache region factory using one of the two options below:
• If the Infinispan CacheManager instance is bound to JNDI (i.e. when deploying within JBoss Application Server) select JndiInfinispanRegionFactory as the cacheregion factory and add the cache manager’s JNDI name:
<!-- If using to JPA, add to your persistence.xml --> <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.infinispan.JndiInfinispanRegionFactory"/> <property name="hibernate.cache.infinispan.cachemanager" value="java:CacheManager"/>
<!-- If using to Hibernate, add to your hibernate.cfg.xml --> <property name="hibernate.cache.region.factory_class"> org.hibernate.cache.infinispan.JndiInfinispanRegionFactory </property> <property name="hibernate.cache.infinispan.cachemanager"> java:CacheManager </property>
• If running JPA/Hibernate and Infinispan standalone or within third party Application Server, select InfinispanRegionFactory as the cache region factory:
<!-- If using to JPA, add to your persistence.xml --> <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.infinispan.InfinispanRegionFactory"/>
<!-- If using to Hibernate, add to your hibernate.cfg.xml --> <property name="hibernate.cache.region.factory_class"> org.hibernate.cache.infinispan.InfinispanRegionFactory </property>
This is all the configuration you need to run JPA/Hibernate with the Infinispan cache provider with the default settings. This should suit the majority of use cases but sometimes, further configuration is required and to help with such situations, please check the following section where more advanced settings are discussed.
Default Configuration Explained
The aim of this section is to explain the default settings for each of the different global data type (entity, collection, query and timestamps) caches, why these were chosen and what are the available alternatives.
Defaults for Entity/Collection Caching
- By default, for all entities and collections, whenever an new entity or collection is read from database and needs to be cached, it's only cached locally in order to reduce intra-cluster traffic. This option cannot be changed.
- By default, all entities and collections are configured to use a synchronous invalidation as clustering mode. This means that when an entity is updated, the updated cache will send a message to the other members of the cluster telling them that the entity has been modified. Upon receipt of this message, the other nodes will remove this data from their local cache, if it is stored there. This option can be changed to use replication by configuring entities or collections to use "replicated-entity" cache but it's not recommended.
- By default, all entities and collections have initial state transfer disabled since there's no need for it. It it's not recommended that this is enabled.
- By default, entities and collections are configured to use READ_COMMITTED as cache isolation level. It would only make sure to configure REPEATABLE_READ if the application evicts/clears entities from the Hibernate Session and then expects to repeatably re-read them in the same transaction. Otherwise, the Session's internal cache provides a repeatable-read semantic. If you really need to use REPEATABLE_READ, you can simply configure entities or collections to use "entity-repeatable" cache.
- By default, entities and collections are configured with eviction settings:
- Eviction wake up interval is 5 seconds.
- Max number of entries are 10.000
- Max idle time before expiration is 100 seconds
You can change these settings on a per entity or collection basis or per individual entity or collection type.
More information in the "Advanced Configuration" section below.
- Finally, by default, both entites and collections are configured with lazy deserialization which helps deserialization when entities or collections are stored in isolated deployments. If you're sure you'll never deploy your entities or collections in classloader isolated deployment archives, you can disable this settting.
Defaults for Query Caching
- By default, query cache is configured so that queries are only cached locally. Alternatively, you can configure query caching to use replication by selecting the "replicated-query" as query cache name. However, replication for query cache only makes sense if, and only if, all of this conditions are true:
- query is quite expensive
- query is very likely to be repeated in different cluster nodes
- and query is unlikely to be invalidated out of the cache (Note: Hibernate must agressively invalidate query results from the cache any time any instance of one of the entity classes involved in the query's WHERE clause changes. All such query results are invalidated, even if the change made to the entity instance would not have affected the query result)
- By default, query cache uses the same cache isolation levels and eviction/expiration settings as for entities/collections.
- By default, query cache has initial state transfer disabled. It is not recommended that this is enabled.
Defaults for Timestamps Cache
- By default, timestamps cache is configured with asynchronous replication as clustering mode. Local or invalidated cluster modes are not allowed, since all cluster nodes must store all timestamps. As a result, no eviction/expiration is allowed for timestamp caches either.
- By default, timestamps is configured with initial state transfer enabled so that joining nodes can get all timestamps. You shouldn't attempt to switch off initial state transfer for timestamps cache.
Infinispan has the capability of exposing statistics via JMX and since Hibernate 3.5.0.Beta4, you can enable such statistics from the Hibernate/JPA configuration file. By default, Infinispan statistics are turned off but when these are disabled via the following method, statistics for the Infinispan Cache Manager and all the managed caches (entity, collection,...etc) are enabled:
<!-- If using to JPA, add to your persistence.xml --> <property name="hibernate.cache.infinispan.statistics" value="true"/>
<!-- If using to Hibernate, add to your hibernate.cfg.xml --> <property name="hibernate.cache.infinispan.statistics">true</property>
The Infinispan cache provider jar file contains an Infinispan configuration file, which is the one used by default when configuring the Infinispan standalone cache region factory. This file contains default cache configurations for all Hibernate data types that should suit the majority of use cases. However, if you want to use a different configuration file, you can configure it via the following property:
<!-- If using to JPA, add to your persistence.xml --> <property name="hibernate.cache.infinispan.cfg" value="/home/infinispan/cacheprovider-configs.xml"/>
<!-- If using to Hibernate, add to your hibernate.cfg.xml --> <property name="hibernate.cache.infinispan.cfg"> /home/infinispan/cacheprovider-configs.xml </property>
For each Hibernate cache data types, Infinispan cache region factory has defined a default cache name to look up in either the default, or the user defined, Infinispan cache configuration file. These default values can be found in the Infinispan cache provider javadoc in [TODOL HIBERNATE JAVADOC LINK]. You can change these cache names using the following properties:
<!-- If using to JPA, add to your persistence.xml --> <property name="hibernate.cache.infinispan.entity.cfg" value="custom-entity"/> <property name="hibernate.cache.infinispan.collection.cfg" value="custom-collection"/> <property name="hibernate.cache.infinispan.query.cfg" value="custom-collection"/> <property name="hibernate.cache.infinispan.timestamp.cfg" value="custom-timestamp"/>
<!-- If using to Hibernate, add to your hibernate.cfg.xml --> <property name="hibernate.cache.infinispan.entity.cfg"> custom-entity </property> <property name="hibernate.cache.infinispan.collection.cfg"> custom-collection </property> <property name="hibernate.cache.infinispan.query.cfg"> custom-collection </property> <property name="hibernate.cache.infinispan.timestamp.cfg"> custom-timestamp </property>
One of the key improvements brought in by Infinispan is the fact that cache instances are more lightweight than they used to be in JBoss Cache. This has enabled a radical change in the way entity/collection type cache management works. With the Infinispan cache provider, each entity/collection type gets each own cache instance, whereas in old JBoss Cache based cache providers, all entity/collection types would be sharing the same cache instance. As a result of this, locking issues related to updating different entity/collection types concurrently are avoided completely.
This also has an important bearing on the meaning of hibernate.cache.infinispan.entity.cfg and hibernate.cache.infinispan.collection.cfg properties. These properties define the template cache name that should be used for all entity/collection data types. So, with the above hibernate.cache.infinispan.entity.cfg configuration, when a region needs to be created for entity com.acme.Person, the cache instance to be assigned to this entity will be based on a "custom-entity" named cache.
On top of that, this finer grained cache definition enables users to define cache settings on a per entity/collection basis. For example:
<!-- If using to JPA, add to your persistence.xml --> <property name="hibernate.cache.infinispan.com.acme.Person.cfg" value="person-entity"/> <property name="hibernate.cache.infinispan.com.acme.Person.addresses.cfg" value="addresses-collection"/>
<!-- If using to Hibernate, add to your hibernate.cfg.xml --> <property name="hibernate.cache.infinispan.com.acme.Person.cfg"> person-entity </property> <property name="hibernate.cache.infinispan.com.acme.Person.addresses.cfg"> addresses-collection </property>
Here, we're configuring the Infinispan cache provider so that for com.acme.Person entity type, the cache instance assigned will be based on a "person-entity" named cache, and for com.acme.Person.addresses collection type, the cache instance assigned will be based on a "addresses-collection" named cache. If either of these two named caches did not exist in the Infinispan cache configuration file, the cache provider would create a cache instance for com.acme.Person entity and com.acme.Person.addresses collection based on the default cache in the configuration file.
Furthermore, thanks to the excellent feedback from the Infinispan community and in particular, Brian Stansberry, we've decided to allow users to define the most commonly tweaked Infinispan cache parameters via hibernate.cfg.xml or persistence.xml, for example eviction/expiration settings. So, with the Infinispan cache provider, you can configure eviction/expiration this way:
<!-- If using to JPA, add to your persistence.xml --> <property name="hibernate.cache.infinispan.entity.eviction.strategy" value= "LRU"/> <property name="hibernate.cache.infinispan.entity.eviction.wake_up_interval" value= "2000"/> <property name="hibernate.cache.infinispan.entity.eviction.max_entries" value= "5000"/> <property name="hibernate.cache.infinispan.entity.expiration.lifespan" value= "60000"/> <property name="hibernate.cache.infinispan.entity.expiration.max_idle" value= "30000"/>
<!-- If using to Hibernate, add to your hibernate.cfg.xml --> <property name="hibernate.cache.infinispan.entity.eviction.strategy"> LRU </property> <property name="hibernate.cache.infinispan.entity.eviction.wake_up_interval"> 2000 </property> <property name="hibernate.cache.infinispan.entity.eviction.max_entries"> 5000 </property> <property name="hibernate.cache.infinispan.entity.expiration.lifespan"> 60000 </property> <property name="hibernate.cache.infinispan.entity.expiration.max_idle"> 30000 </property>
With the above configuration, you're overriding whatever eviction/expiration settings were defined for the default entity cache name in the Infinispan cache configuration used, regardless of whether it's the default one or user defined. More specifically, we're defining the following:
- All entities to use LRU eviction strategy
- The eviction thread to wake up every 2000 milliseconds
- The maximum number of entities for each entity type to be 5000 entries
- The lifespan of each entity instance to be 600000 milliseconds
- The maximum idle time for each entity instance to be 30000
You can also override eviction/expiration settings on a per entity/collection type basis in such way that the overriden settings only afftect that particular entity (i.e. com.acme.Person) or collection type (i.e. com.acme.Person.addresses). For example:
<!-- If using to JPA, add to your persistence.xml --> <property name="hibernate.cache.infinispan.com.acme.Person.eviction.strategy" value= "FIFO"/> <property name="hibernate.cache.infinispan.com.acme.Person.eviction.wake_up_interval" value= "2500"/> <property name="hibernate.cache.infinispan.com.acme.Person.eviction.max_entries" value= "5500"/> <property name="hibernate.cache.infinispan.com.acme.Person.expiration.lifespan" value= "65000"/> <property name="hibernate.cache.infinispan.com.acme.Person.expiration.max_idle" value= "35000"/>
<!-- If using to Hibernate, add to your hibernate.cfg.xml --> <property name="hibernate.cache.infinispan.com.acme.Person.eviction.strategy"> FIFO </property> <property name="hibernate.cache.infinispan.com.acme.Person.eviction.wake_up_interval"> 2500 </property> <property name="hibernate.cache.infinispan.com.acme.Person.eviction.max_entries"> 5500 </property> <property name="hibernate.cache.infinispan.com.acme.Person.expiration.lifespan"> 65000 </property> <property name="hibernate.cache.infinispan.com.acme.Person.expiration.max_idle"> 35000 </property>
The aim of these configuration capabilities is to reduce the number of files needed to modify in order to define the most commonly tweaked parameters. So, by enabling eviction/expiration configuration on a per generic Hibernate data type or particular entity/collection type via hibernate.cfg.xml or persistence.xml, users don't have to touch to Infinispan's cache configuration file any more. We believe users will like this approach and so, if you there're any other Infinispan parameters that you often tweak and these cannot be configured via hibernate.cfg.xml or persistence.xml, please let the Infinispan team know by sending an email to email@example.com
Finally, please note that query/timestamp caches work the same way they did with JBoss Cache based cache providers. In other words, there's a query cache instance and timestamp cache instance shared by all. It's worth noting that eviction/expiration settings are allowed for query cache but not for timestamp cache. So configuring an eviction strategy other than NONE for timestamp cache would result in a failure to start up.