Caching with JCS

Do you need to implement some data caching in your project to help speed up retrieval from a slow data source?  Consider using JCS to bring the data a little closer to the users.  JCS is an in-memory object cache that has auxiliary caches available for larger data sets and for remote caching.  The cache is very configurable and the project owners claim it is faster than its competition.

JCS is an Apache project, so you know it is a high quality library with flexible licensing.  The cache itself resembles a map-like data structure.  Data is added and retrieved using puts and gets, just like the Java Map interface.  The overall cache is broken down into regions, where there is a default region, and each region can be optionally broken down into groups.  Regions and groups are a great way to cache related data together.  While remote caching is one of the features of JCS, this post will only deal with in-memory and disk caching.

Using JCS requires the JCS jar, commons logging, and the concurrent library from Doug Lea.  Links to these can be found at the end of this post.

The main cache configuration file is named cache.ccf.  You start by configuring a default cache and any cache regions you will need.


jcs.default=
jcs.default.cacheattributes=org.apache.jcs.engine.CompositeCacheAttributes
jcs.default.cacheattributes.MaxObjects=200
jcs.default.cacheattributes.MemoryCacheName=org.apache.jcs.engine.memory.lru.LRUMemoryCache
jcs.default.elementattributes.IsEternal=false
jcs.default.elementattributes.MaxLifeSeconds=86400
jcs.default.elementattributes.IdleTime=86400

This configures a basic, in-memory cache that will hold 200 objects that expire after one day.  After 200 objects are loaded, the least recently used items are evicted from the cache.

Adding a region is very similar:


jcs.region.contactCache=
jcs.region.contactCache.cacheattributes=org.apache.jcs.engine.CompositeCacheAttributes
jcs.region.contactCache.cacheattributes.MaxObjects=1000
jcs.region.contactCache.cacheattributes.MemoryCacheName=org.apache.jcs.engine.memory.lru.LRUMemoryCache
jcs.region.contactCache.elementattributes.IsEternal=false
jcs.region.contactCache.elementattributes.MaxLifeSeconds=86400
jcs.region.contactCache.elementattributes.IdleTime=86400

This configuration also creates an in-memory cache called contactCache.  It will hold up to 1000 items, most likely related to contact information in your application.

If you have large objects or many objects to cache, holding them all in memory may not be appropriate or feasible.  JCS features a disk cache where objects are spooled out to to the disk when the maximum memory settings are reached.

Setting up a disk cache is also pretty easy.  Add this to the end of the cache.ccf file:


jcs.auxiliary.DC=org.apache.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory
jcs.auxiliary.DC.attributes=org.apache.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes
jcs.auxiliary.DC.attributes.DiskPath=/some/path
jcs.auxiliary.DC.attributes.MaxPurgatorySize=100
jcs.auxiliary.DC.attributes.MaxKeySize=-1
jcs.auxiliary.DC.attributes.OptimizeAtRemoveCount=5000
jcs.auxiliary.DC.attributes.MaxRecycleBinSize=100

Then set the contact cache to use the disk cache:


jcs.region.contactCache=DC #this is a replacement of the first line in the contactCache configuration from above

Once the contactCache reaches 1000 items (based off of the MaxObjects setting), the least recently used items will start to spool off into the disk cache.  Make sure the objects implement Serializable.  The items on disk will continue to comply with the expiration settings and retrieving them is completely seamless using the normal methods.

The cache.ccf file should be in the classpath where JCS will find it automatically.  Setting everything up in a Spring project is pretty easy once the cache.ccf file is created.

Define a bean in the applicationContext.xml file:


<bean id="contactCache" class="org.apache.jcs.JCS" factory-method="getInstance">
    <constructor-arg>
        <value>contactCache</value>
    </constructor-arg>
</bean>

Then inject the cache bean into the classes accessing the cache using annotations (or a setter method if you are into that):


@Autowired
@Qualifier("contactCache")
private JCS contactCache;

Somewhere along the way, the cache needs to be shut down gracefully.  A commonly suggested place to do this in a web app is to put it in a filter destroy() method.  But any hook that is tied into a shutdown event should be fine.


CompositeCacheManager.getInstance().shutDown(); //stop JCS properly

A graceful shutdown when using a disk cache will properly write out the keys and values to disk, so they can be used when the application starts back up.  Keys are held in memory and will be lost if the JVM process is ended abruptly.

Basic usage of the cache is like this:

  • Check the cache to see if the item exists
  • If it doesn’t, create it and add it to the cache
  • Use the item

Here is some simple code to implement this:


Contact johnContact = (Contact)contactCache.get(“john”);
if (johnContact == null) {
    johnContact = slowDataSource.getContact(“john”);
    contactCache.put(“john”, johnContact);
}
return johnContact;

Breaking the cache region down into groups is a great way to divide employee contact information from customer contact information.


contactCache.putInGroup("johnEmployee", "employees", johnContact);
contactCache.putInGroup("sueCustomer", "customers", sueContact);

Contact john = (Contact)contactCache.getFromGroup("johnEmployee", "employees");
Contact sue = (Contact)contactCache.getFromGroup("sueCustomer", "customers");

Putting contacts in groups also allows them to be retrieved as a group, where just using them in the general contacts cache does not.  For example:


for (Object key : contactCache.getGroupKeys("employees")) {
    Contact c = (Contact)contactCache.getFromGroup(key, "employees");
    //Do something like add contact to a list to return to a controller or view
}

Of course it might make sense to just store a list of contacts that will be used by the view layer of the application:


List<Contact> empList = slowDataSource.getAllEmployees();
contactCache.put(“employeeList”, empList);

Data can be stored in any way that makes sense as long as the objects stored are serializable.

JCS provides a simple JSP for viewing details about the current state of the cache.  The JSP should be available in the download, otherwise there is a link to it at the end of this post.  The URLs in the JSP may need to be edited for it to work correctly depending on how your application is setup.

Implementing data caching in an application typically opens up a huge can of worms.  Take these things into consideration when deciding if caching is a viable solution.

  • Can slow data retrieval be optimized in other ways?  If optimizing a query or horizontally scaling a datastore will speed up retrieval, look into it.
  • How often is the data targeted for caching updated?  Caching is great for data that is read often but rarely updated so it shouldn’t be used with transactional data that can be updated at any time or very often.
  • How big is the data to be cached?  Data caches are not meant for extremely large objects or extreme numbers of objects.  There is a point where a data cache cannot save an application from the size of the data.
  • How important is it to persist the cached data?  Generally speaking, cached data should not be persisted at all!  If it’s absolutely necessary for cached data to be fault tolerant and ACID compliance to be adhered to, there is probably a better solution than JCS and general data caching might not be what you are after.
  • Is the data sensitive?  You might need to encrypt the data before caching it which could cost more that just serving up the data.
  • Should the data be consistent across servers?  If so, take a look at the lateral and remote caching provided by JCS and other data cache providers.

JCS has many more features like remote caching, JDBC caches, and event handling.  This post will get you started with the most common use case of JCS, which is the in-memory cache.

Links

Downloads

One thought on “Caching with JCS

  1. manoj says:

    How jcs know the data updated or modified in the data base. for example I fetch data from database and I catched that data in cache. Now the table is udated or some of the records or inserted or deleted, Then how will cache know the data get modified in Database. How we have to handle this scenarios. This creates an inconsistent data in application. Please provide an approach how we can solve this scenarios. Thanks in advance.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

*