Skip navigation

Integrating Terracotta DSO with EHCache - A Simple Example

Article Rating?





Introduction

This document shows you how to cluster a simple web application by integrating Terracotta DSO with EHCache.

  1. Write a simple example application.
  2. Download Terracotta DSO and the EHCache Terracotta integration module (TIM).
  3. Configure Terracotta DSO.
  4. Run in a cluster.

For further reading, see the following sections at the end of this document:

The Application

Let's pretend that in our application we are managing a music collection. We'll further assume that we're building an app to manage not just our own music collection, but the collections of many people in some sort of magical web 2.0 shared application. In this case, our application servers will need to hold lots of album data in memory at any given time. Many people will have the same popular albums so it makes sense to stick these albums in an application cache, rather than load them from a data source every time.

We'll need some kind of Album class:

package albums;
public class Album implements Serializable {
   private int    id;
   private String title;
   private String artist;
   private int    year;

   // all the obvious constructors, getters,
   // equals, hashCode, toString(), etc
}

We then need to create a front-end to our cache (hiding the use of EHCache from the users of our cache). Let's assume all we need is the ability to add and find albums. Note that our application has <hand-wave/> assigned a unique identifier to each album added to the system so we can use that for identification purposes.

package albums;
public class AlbumCache {
   private CacheManager manager = new CacheManager();

   private Cache getCache() {
     return manager.getCache("albumCache");
   }

   public void addAlbum(Album album) {
     Cache albumCache = getCache();
     albumCache.put(new Element(album.getId(), album));
   }

   public Album findAlbum(int id) {
     Cache albumCache = getCache();
     Element element = albumCache.get(id);
     if (element != null) {
       return (Album) element.getValue();
     } else {
       return null;
     }
   }
}

AlbumCache doesn't really do much – we have defined an EHCache CacheManager and the getCache() method looks up our particular cache. There are several ways to create CacheManagers and here we are just using a very simple one that loads the cache configuration from a resource named ehcache.xml in the classpath. More on that in a moment. We then just have simple methods that add an album (keyed by album ID) to the cache and look up an album in the cache by ID (if it exists).

Next up we need to look at the ehcache.xml file:

<ehcache
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="ehcache.xsd">

        <cache         name="albumCache"
                        maxElementsInMemory="10000"
                        memoryStoreEvictionPolicy="LFU"
                        eternal="false"
                        timeToIdleSeconds="300"
                        timeToLiveSeconds="600"
                        overflowToDisk="false"
                        diskExpiryThreadIntervalSeconds="120" />

        <defaultCache
                        maxElementsInMemory="10000"
                        memoryStoreEvictionPolicy="LRU"
                        eternal="false"
                        timeToIdleSeconds="120"
                        timeToLiveSeconds="120"
                        overflowToDisk="true"
                        diskSpoolBufferSizeMB="30"
                        maxElementsOnDisk="10000000"
                        diskPersistent="false"
                        diskExpiryThreadIntervalSeconds="120" />
                        
</ehcache>

Every EHCache configuration file requires a defaultCache definition although we aren't using it in our application. We'll be using the first named cache "albumCache". Most of this configuration should be straightforward – the cache allows 10000 items at most and does not overflow to disk. Items in the cache are evicted based on a "Least Frequently Used" algorithm when the cache is full and expire when unused for 300 seconds or at most 600 seconds.

We can then write a simple driver program to load and retrieve some data from cache:

public class Main {
   public static void main(String arg[]) throws Exception {
     AlbumCache cache = new AlbumCache();
        findAlbums(cache);
     loadAlbums(cache);
     findAlbums(cache);
   }

   private static void loadAlbums(AlbumCache cache) {
     System.out.println();
     System.out.println("Adding albums.");
     cache.addAlbum(new Album(0, "Moving Pictures", "Rush", 1981));
     cache.addAlbum(new Album(1, "What's Going On", "Marvin Gaye",  
1971));
     cache.addAlbum(new Album(2, "The White Album", "The Beatles",  
1968));
     cache.addAlbum(new Album(3, "The Dark Side of the Moon", "Pink  
Floyd", 1973));
   }

   private static void findAlbums(AlbumCache cache) {
     System.out.println();
     System.out.println("Finding albums:");
     for(int i=0; i<4; i++) {
       System.out.println(cache.findAlbum(i));
     }
   }
}

This simple program loads some classic albums and then finds them again in the cache by ID. To run this program, you'll need the EHCache jar (we'll use 1.3.0) and the commons-logging jar. Execution may look something like this:

$ java -cp bin:lib/ehcache-1.3.0.jar:lib/commons-logging-1.1.1.jar  
albums.Main

Finding albums:
null
null
null
null

Adding albums.

Finding albums:
Moving Pictures by Rush (1981)
What's Going On by Marvin Gaye (1971)
The White Album by The Beatles (1968)
The Dark Side of the Moon by Pink Floyd (1973)

But what happens when our music collection application hits the front page of digg? We gotta scale up. That of course is a whole slew of topics in itself, but let's look at how we can scale our EHCache instance with Terracotta DSO. Terracotta DSO allows us to mark portions of our Java heap as shared and those portions then become visible to every node in the Terracotta cluster. Not only that, but the clustered data can be modified and the changes will become visible in every node in the cluster as well.

Download Terracotta DSO and the EHCache TIM

Use the following links to learn about downloading and installing Terracotta DSO and the EHCache TIM:

Configure Terracotta DSO

We're now ready to configure our application to run with Terracotta DSO and clustered EHCache. Terracotta DSO works by supplying external configuration and modifying the java startup process, rather than by you calling an API. Thus, we will leave our application unchanged.

Instead we must create a tc-config.xml configuration file, which defines what is clustered. In this program, we need to indicate that the Album class will be in the clustered heap and must be instrumented. Also, we must mark the AlbumCache.manager field as being a clustered root. In Terracotta DSO, roots indicate a starting point for clustered state. All object references from the root will be clustered (unless you use transient to mark fields that should not be clustered).

Also, we must include the ehcache-1.3 integration module which will pull in the configuration defined in the TIM we downloaded.

<?xml version="1.0" encoding="UTF-8"?>
<con:tc-config xmlns:con="http://www.terracotta.org/config">
   <servers>
     <server host="your.ip.goes.here" name="localhost">
       <dso-port>9510</dso-port>
       <jmx-port>9520</jmx-port>
       <data>terracotta/server-data</data>
       <logs>terracotta/server-logs</logs>
       <statistics>terracotta/cluster-statistics</
statistics>
     </server>
     <update-check>
       <enabled>true</enabled>
     </update-check>
   </servers>
   <clients>
     <logs>terracotta/client-logs</logs>
     <statistics>terracotta/client-statistics/%D</
statistics>
     <modules>
       <module group-id="org.terracotta.modules" name="tim-
ehcache-1.3" version="1.1.1"/>
     </modules>
   </clients>
   <application>
     <dso>
       <instrumented-classes>
         <include>
           <class-expression>albums.Album</class-expression>
         </include>
       </instrumented-classes>
       <roots>
         <root>
           <field-name>albums.AlbumCache.manager</field-
name>
         </root>
       </roots>
     </dso>
   </application>
</con:tc-config>

We also need to add one additional jar to our classpath – this jar is currently required by the EHCache TIM (this dependency should be removed in the future). The jar to include is the JSR 107 (JCache) api jar. This api can optionally be used by EHCache, and Terracotta DSO provides support for it. You can download the project from the JSR 107 SourceForge project.

Run it!

Before we run our program again with Terracotta DSO, we need to first start a Terracotta server. There are a variety of ways you can configure the Terracotta server for high availability, persistence, etc. Here we'll just start a single server with a script provided in <TC>/bin:

$ start-tc-server.sh

The easiest way to start the application is to use the dso-java.sh script. This script just wraps the java command indicated by your JAVA_HOME environment variable to set a few properties and prepend the dynamically generated Terracotta boot jar to the bootclasspath.

$ dso-java.sh \
-cp bin:lib/ehcache-1.3.0.jar:lib/commons-logging-1.1.1.jar:lib/
jsr107cache-1.0.jar \
albums.Main

Finding albums:
null
null
null
null

Adding albums.

Finding albums:
Moving Pictures by Rush (1981)
What's Going On by Marvin Gaye (1971)
The White Album by The Beatles (1968)
The Dark Side of the Moon by Pink Floyd (1973)

Here we see that we get the same results as when running it without Terracotta DSO. But if we run the program again, you'll see that the first find section will actually find all albums. The state in the album cache has been clustered and stored in the Terracotta server.

If you start up multiple JVMs in this manner, they will all be sharing the identical clustered version of EHCache. When an item is added to the cache, it's seen by every JVM in the cluster.

More on Configuration

You might be wondering how to configure the Terracotta version of EHCache. For this, we'll need to look back at the EHCache configuration file. Terracotta DSO integrates with EHCache by providing a custom memory store. You can configure that memory store using the same properties used in ehcache.xml: timeToLiveSeconds, timeToIdleSeconds, and diskExpiryThreadIntervalSeconds.

These properties control a time-based eviction policy used in place of the normal LRU/LFU/FIFO eviction policies. This time-based store is designed to maximize concurrency and minimize the need to fault objects into a cluster node for eviction. One key point is that the Terracotta distributed EHCache is coherent – all nodes are seeing the same picture of the cache.

Resources

See the following for more information:

Adaptavist Theme Builder Powered by Atlassian Confluence