Tuesday, July 21, 2009

Using SelfPopulatingCache in Ehcache

Introduction
Often you can notice that Ehcache is used mostly like a tool that implements highly configurable maps. Sometimes developers configure time-to-live properties, or make use of disk-store functionality, but you can rarely meet someone who has cache population/eviction process that makes sense.

In the perfect world I would expect cache to be a universal pool. I request a value for a key and would like to get it whenever it is possible and as fast as possible with just one line of code. But in reality I usually can see huge cache population code on the application startup, daemon threads that update caches in eternal loop and numerous “ifs” around cache.get() calls.

In my strive to the perfect world I’ve discovered a SelfPopulatingCache class in Ehcache. In this article I will describe how using SelfPopulatingCache class one can implement a cache with self creating objects with optional auto-updating. In some way this example is an implementation of ideas mentioned in Ehcache documentation.

Example
What you will see down here:
  • A Reader that fetches an object from cache 5 times every 0.5 seconds.
  • Behind scenes cache will create a new object if there is no such object is there in cache for a key requested.
  • A daemon thread will trigger cache refresh every 2 seconds.
The most important class in this example is ExampleCacheProvider.



What it does is creating simple Ehcache (original cache) and wrapping it with SelfPopulatingCache (selfPopulatingCache). We can use this class the same way as CacheManager, so one can consider extending CacheManager, but to keep it simple here we'll stick to this kind of cache source.
While wrapping we specify updatingFactory, which can be set to one of two options: ExampleCacheEntryFactory (line 19) and ExampleUpdatingCacheEntryFactory(line 21). For the purposes of this example the choice between this two options is done based on the “com.blogspot.mikler.java.cache.factory” system property value. The difference between this two is that ExampleCacheEntryFactory implements CacheEntryFactory interface, while ExampleUpdatingCacheEntryFactory extends ExampleCacheEntryFactory and implements UpdatingCacheEntryFactory. The difference between using each of this options is explained later on. Meanwhile here is the code of both classes.



ExampleUpdatingCacheEntryFactory :



As you can see in this example as cache element’s key string used, while StringBuffer is used as a value. In createEntry() method in ExampleCacheEntryFactory StringBuffer is created with leading random number. While in updateEntryValue() method of ExampleUpdatingCacheEntryFactory existing StringBuffer length is appended to the buffer itself.

And finally, here goes our Reader main class. It fetches our wrapped cache, get’s value for “foo” key from it and stores it into final local variable fooOriginalBuffer, that is never changed late in Reader’s code. Then it starts doing 5 iterations of getting value for “foo” key from cache, displaying debug info and stats, and sleeping for one second. The code is simple as this.



Let’s run it with both updatingFactory options.
Output of running the Reader with ExampleCacheEntryFactory (-Dcom.blogspot.mikler.java.cache.factory=create)


And here how the output looks like while running Reader with ExampleUpdatingCacheEntryFactory as updatingFactory (-Dcom.blogspot.mikler.java.cache.factory=update)


Conclusion
As you can see the output is quite different and here are some conclusions we come to from analyzing it:
  • No NullPointerException is occurs while trying to get object from cache that is not there. It is being created in both cases.
  • If updatingFactory is instance of CacheEntryFactory (ExampleCacheEntryFactory in our case) when cache.refresh() is called each object in cache is being recreated.
  • Also when updatingFactory is instance of CacheEntryFactory final fooOriginalBuffer variable is not updated.
  • Meanwhile in case when updatingFactory is instance of CacheEntryFactory (UpdatingCacheEntryFactory in our case) when cache.refresh() is called each object in cache gets updated instead of being recreated.
  • And final fooOriginalBuffer variable value is updated as well. (Actually this variable itself is passed to updateEntryValue() method of ExampleUpdatingCacheEntryFactory)

Besides the above conclusion, one can notice that using SelfPopulatingCache reduces scattering and tangling (see my “WTF is AOP article”).

As usually, you can get source code for this post by running
svn co http://miklerjava.googlecode.com/svn/trunk/samples/SelfUpdatingEhCache SelfUpdatingEhCache

No comments: