Saturday, March 10, 2007

Caching with EhCache

We are in the process of switching out the current caching library in our web application, ShiftOne Java Object Cache (JOCache), with ehcache. Both are open source caching solutions, but ehcache offers significantly more features than JOCache. The original choice was made when there were fewer solutions to choose from, and as often happens with open source projects, it is easy to back the wrong horse.

Let me qualify that. When I say wrong horse, its not a reflection on either project. An open source project is typically born out of the author's own needs. It is a testament to the author's generosity that he shares his code with the world. Sometimes, the author may not be motivated to do more to the code once his own needs are met, or maybe his day job keeps him too busy, while others may thrive on the user input and feedback and are motivated to add more features to the code. Some lucky open source authors may even get to work for companies where they spend all or part of their time improving their project. Whatever the circumstances, from the point of view of a user of an open source solution, predicting which solution will grow with your needs is pretty much a crapshoot.

Our decision to switch arose out of the necessity to extend the scope of our current caching. JOCache only provides functionality to cache objects in memory, and expire them out based on the specified caching policy. A small part of our application used caching, but because there is no built in functionality to write out expired entries to a secondary disk cache, we (my predecessors, not me) built this functionality locally. Unfortunately, they also implemented a custom serialization mechanism for these objects, so this forces all new objects that need to use this setup to also implement similar functionality, or forgo secondary disk caching. Fortunately, the code to switch out caches is isolated to a single class, so its just a matter of replacing the JOCache implementation with an ehcache implementation.

Our decision to go with ehcache (instead of, say OSCache or Apache JCS) was somewhat arbitary, based on the previous experience of some of our developers with ehcache embedded in Hibernate. However, a quick look at the websites for most popular open source caching solutions reveal that they all offer similar functionalty.

I had never used ehcache by itself before. I needed to get familiar with its API, so I decided to see if I could apply caching with ehcache to one of the embedded databases I had tested and reported on in my blog post couple of weeks ago. The worst performer among the embedded databases was Apache Derby, so that is the one I chose for my test. Here is the implementation of IEmbeddedADb for Derby with ehcache.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class CachedDerbyEmbeddedDb extends DerbyEmbeddedDb implements IEmbeddedDb {

  private Cache cache;

  public CachedDerbyEmbeddedDb() throws Exception {
    super();
    CacheManager cacheManager = CacheManager.create("src/main/resources/ehcache.xml");
    String[] names = cacheManager.getCacheNames();
    cache = cacheManager.getCache("derby-cache");
  }

  public String get(String key) {
    String value = null;
    try {
      Element cachedElement = cache.get(key);
      if (cachedElement != null) {
        value = (String) cachedElement.getValue();
      }
      if (value == null) {
        value = super.get(key);
        cache.put(new Element(key, value));
      }
    } catch (CacheException e) {
      // fall back to database
      LOGGER.warn("Cache failure!", e);
      value = super.get(key);
    }
    return value;
  }
}

The derby-cache is cached in the ehcache.xml file like so:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<ehcache>
  <diskStore path="java.io.tmpdir"/>

  <defaultCache ...>

  <cache
    name="derby-cache"
    maxElementsInMemory="10000"
    eternal="false"
    timeToIdleSeconds="120"
    timeToLiveSeconds="120"
    overflowToDisk="true"
    diskPersistent="false"
    diskExpiryThreadIntervalSeconds="120"/>
  ...
</ehcache>

After caching was applied, the query time to execute 1000 random queries against this database dropped from 2013 ms to 99 ms, bringing it to third place from fifth, up right behind HSQLDB cached tables with a response time of 77 ms for 1000 random queries.

Be the first to comment. Comments are moderated to prevent spam.