Data Caching

Scooter provides a few server-side data caching stores.

Data caching within the same request thread

By default, data accessing using ActiveRecord will have data cached in the same request thread. This option is set by the useRequestCache property in environment.properties file.

useRequestCache=true

Therefore if your code calls Post.findById(100) multiple times, only the first time this method retrieves data from the underlying database, the rest of times it just returns data from the cache.

The useRequestCache property is set for all ActiveRecord model classes. If for some reason you don't want to cache data for a specific method, you just tun it off by listing it in localUseCacheExceptions property. For example:

localUseCacheExceptions=com.blog.model.Post.findAll, com.blog.model.Post.findFirst

The above setting simply says do not cache the result of findAll and findFirst in Post model.

Data caching across requests: second-level cache

Second-level cache allows applications to cache data across requests.

By default, second-level cache is not turned on. To turn it on, just set these in environment.properties file. For example:

useSecondLevelCache=true
default.cache.provider.name=blog_development

plugin.cache.provider.blog_development=\
    plugin_class=plugin.ehcache.EhCacheCacheProvider,\
    configFile=ehcache.xml

This tells Scooter to use EhCache plugin for second-level cache and use ehcache.xml as its configuration file.

The file ehcache.xml is the ehcache configuration file. It can be put under blog/WEB-INF/config directory, or you can modify the one under scooter/plugins/ehcache/src. You can even specify an absolute path for the file in the configFile property. The plugin will find it out.

Cache properties

Properties for Cache setting:

PropertyDescriptionDefaultExample
useRequestCache Cache data in request thread true
useSecondLevelCache Cache data in second-level cache false
allowCacheAssociatedObjects Cache data of assoicated objects false
flushCacheOnChange Flush cache when change (update/create/delete) happens true
localUseCacheExceptions Comma deliminated full method names to turn off cache setting for the listed methods Empty This will not cache result of findAll and findFirst methods:
localUseCacheExceptions = com.blog.model.Post.findAll, com.blog.model.Post.findFirst
localFlushCacheExceptions Comma deliminated full method names to turn off flush setting for the listed methods Empty This will keep result of findAll and findFirst forever which is good for read-only application:
localFlushCacheExceptions = com.blog.model.Post.findAll, com.blog.model.Post.findFirst

Cache provider plugins

Config a plugin

Configuration of cache providers follows the style of configuring database connections. Scooter automatically provides default configuration for DEVELOPMENT, TEST and PRODUCTION environments at project creation time. You can then modify them in environment.properties file. If you do not need them, just keep them commented or removed from the property file.

Here are the standard parameters you can configure for a cache-provider plugin:

PropertyDescriptionRequired?
plugin_class Plug-in class name Y
namespace Namespace of a memcached store N
urls memcached server urls. Multiple urls can be listed here separated by space Y
expiresInSeconds specifies how long should the object stay in cache N
requestTimeoutInSeconds specifies how long should a request waits for response from cache server N

You can also add your own parameters as follows:

plugin.cache.provider.jpetstore=\
    plugin_class=com.example.MyCacheProvider,\
    namespace=jpetstore,\
    urls=localhost:11211,\
    myproperty1=value1,\
    myproperty2=value2

Here are generated definitions of cache-provider plugins:

plugin.cache.provider.blog_development=\
    plugin_class=plugin.ehcache.EhCacheCacheProvider,\
    configFile=ehcache.xml,\
    useSerialization=true

plugin.cache.provider.blog_test=\
    plugin_class=plugin.spymemcached.SpyMemcachedCacheProvider,\
    namespace=blog,\
    urls=localhost:11211,\
    expiresInSeconds=3600,\
    requestTimeoutInSeconds=60

plugin.cache.provider.blog_production=\
    plugin_class=plugin.spymemcached.SpyMemcachedCacheProvider,\
    namespace=blog,\
    urls=localhost:11211 localhost:11212,\
    expiresInSeconds=3600,\
    requestTimeoutInSeconds=60

You can create and use your own cache-provider plugin too by following the way the included memcached plugin, e.g. plugin.ehcache.EhCacheCacheProvider, is implemented.

Scooter provides a pluggable second-level caching mechanism. It includes the following plugins for second-level cache:

EhCache Plugin

Scooter supports EhCache with a EhCache plugin.

plugin.cache.provider.blog_development=\
    plugin_class=plugin.ehcache.EhCacheCacheProvider,\
    configFile=ehcache.xml, \
    useSerialization=true, \
    useDefaultCacheNameIfAbsent=true, \
    defaultCacheName=MyAppName

Besides the properties mentioned above for all plugins, Scooter also provides the following properties for EhCache:

PropertyDescriptionDefaultExample
useSerialization Data stored are serialized.
If you want to prevent multiple users to update the same instance, set this property to true.
false
useDefaultCacheNameIfAbsent If true, a default cache name is used.
In this way, you do not need to define all caches for all models.
false
defaultCacheName Default cache name Application Name

Memcached Plugins

Scooter supports Danga's memcached.

Currently two memcached clients are included as cache-provider plugins: spymemcached and xmemcached. You can find them in the plugins directory.

Note: In order to test memcached-backed Mem Cache, you need to start a memcached server--just like starting up MySQL or Oracle database server when testing database applications. North Scale labs provides a build of memcached for Windows.

Convenient cache classes

A few classes allow you to cache data within the same request thread.

1. CurrentThreadCache class

CurrentThreadCache class allows caching data for the same request thread. It provides convenient static methods for caching:

public static Object get(String key);
public static void set(String key, Object value);
public static void clear(String key);
public static void clear();

By using CurrentThreadCache class, you can pass data obtained in controller class to model class without declaring the data type in the model class' API.

CurrentThreadCache is created when server receives a request and destroyed when the server sends a response. If you'd like to store data that can be used in more than one request, you should use CacheStore.

2. NamedCurrentThreadCache class

NamedCurrentThreadCache class allows caching data for the same request. It takes a name for the cache. In this way, you can save data to a cache with a specific name. For example:

Cache modelCache = new NamedCurrentThreadCache(ModelClass.getName());;

3. DefaultCacheStore class: second-level caching made easy

You can choose one cache provider as a default cache provider and use DefaultCacheStore class' static methods to access it.

For example, add the following line in environment.properties file, where blog_development is a cache provider named plugin.cache.provider.blog_development defined in the example above.

default.cache.provider.name=blog_development

Then you can use convenient static methods provided by DefaultCacheStore class:

public static Object get(Object key);
public static boolean put(Object key, Object obj);
public static boolean remove(Object key);
public static void clear();