I am trying to migrate my project code from OSCache to EhCache.
We have used OSCache not only as a second-level Hibernate cache provider but also to store other objects of a different nature. They all happily shared the same cache instance without any collisions due to non-overlapping cache keys.
One big difference when moving towards EhCache is that each region has its different cache instance. This is potentially good as it can improve lookup speed as data of the different nature resides separately. Unfortunately, this has a price of configuration hell. Let me explain.
In the OSCache world, I would configure my cache capacity to be, let's say, 10000. Now if a particular installation would require/could afford more RAM, I would easily beef it up to 50000 and that would do. Now in EhCache I have to go and change the setting by portion of this delta for every region!
Moreover, one installation might have higher usage of objects of type X whereas another installation might prefer higher churn of objects of type Y. We have dozens of installations and each installation would have hundreds of different caches. For this, we would have to hire bunch of people just doing nothing but monitoring cache patterns and tweaking the settings!
I was expecting CacheManager
to have some sort of a global cache capacity setting and each internal cache would fight for more capacity, depending on entry usage. However the only way I found to set the cache capacity is via CacheConfiguration
which is many-to-one against CacheManager
.
So far the only option I can see is to try to force the Hibernate to use one global cache for all the entities. Does anybody know how to do that? Are there any other, better, solutions for my scenario?
You can try having one single cache and adding decorators around it. The decorators can have names matching your region names so that hibernate can use those caches but those decorators would be using the same cache underneath. So theres only one cache config to manage.
You can achieve this by implementing Custom cache decorators and set up the names of your decorated caches.
You can have ehcache.xml something like this:
<defaultCache maxElementsInMemory="10000" eternal="false"
overflowToDisk="false"/>
<cache name="singleSharedCache" maxElementsInMemory="2000"
eternal="false" overflowToDisk="false">
<cacheDecoratorFactory class="com.xyz.util.CustomEhcacheDecoratorFactory"
properties="name=org.hibernate.tutorial.domain.Person" />
<cacheDecoratorFactory class="com.xyz.util.CustomEhcacheDecoratorFactory"
properties="name=org.hibernate.tutorial.domain.Event" />
</cache>
The "com.xyz.util.CustomEhcacheDecoratorFactory" is a custom ehcache decorator factory class which is used to create the decorated ehcaches. You can use the "properties" attribute to set up the decorated ehcache in any way you want, here you only use a name property to configure the name of the new decorated ehcache. All other operations can be delegated to the underlying cache.
Providing one custom cache decorator that would work for this use-case here, it reuses the EhcacheDecoratorAdapter that comes in the ehcache jar and just overrides getName(). EhcacheDecoratorAdapter delegates all operations to an underlying ehcache which you pass in the constructor:
package com.xyz.util;
import java.util.Properties;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.constructs.CacheDecoratorFactory;
import net.sf.ehcache.constructs.EhcacheDecoratorAdapter;
public class CustomEhcacheDecoratorFactory extends CacheDecoratorFactory {
public Ehcache createDecoratedEhcache(final Ehcache cache,
final Properties properties) {
return new EhcacheDecoratorAdapter(cache) {
private final String name = properties.getProperty("name");
public String getName() {
return name;
}
};
}
public Ehcache createDefaultDecoratedEhcache(final Ehcache cache,
final Properties properties) {
return new EhcacheDecoratorAdapter(cache) {
private final String name = properties.getProperty("name");
public String getName() {
return name;
}
};
}
}