The Carbon Java Framework  

The Carbon Cache Module

Cache Service Design

Printer Friendly Version

Author: Greg Hinkle (ghinkle at sapient.com)
Version: $Revision: 1.6 $($Author: jdreed $ / $Date: 2003/10/29 18:34:03 $)
Created: March 2003

Overview

The Cache Service provides a high-speed, thread-safe repository of information to a JVM without incurring the cost of a remote call. A client of the Cache Service is able to perform only very simple functions such as getting a value or requesting that the cache be refreshed. However, behind the scenes, the cache may need to periodically refresh the underlying data from the backing store. All of this should be accomplished in a thread-safe manner with as much efficiency as possible to produce a solution that is fast and accurate.

Cache Types

Total Cache

A total cache, is a cache which needs to be refreshed, as a whole, on a periodic basis. The intended use of this cache is to provide fast access to a small set of data, such as lookup values. The data all must become stale at the same time, as the cache is refreshed as a whole.

There are two different implementations of total caches: read-only and writable. A read-only cache implementation is supplied for cases where data is cached for read-only purposes. With the excpetion of cache refreshes, the data cannot be modified. This is faster than writable caches because synchronization restrictions are not as stringent. A writable cache is supplied for cases wherein the cached data can be changed by clients of the cache. Note that this implementation is not cluster-aware, so changes are only visible within the cache's local JVM. Also, this is not a write-through cache (the backing data-store is not affected on updates) so changes will be lost when the cache is refreshed.

Most Recently Used (MRU) Cache

The MRU Cache (Most Recently Used Cache) is a cache which has a set of data that is larger than the cache itself. The cache operates in what is known alternately as a Most Recently Used (MRU) or uses a Least Recently Used (LRU) algorithm. What this means is that the most recently requested items are kept in the cache, while the least recently used are cached only briefly, and tend to be fetched on each request. An MRU cache is very useful for data which it is even slightly expensive to retrieve, and which conforms to the 80/20 rule - 20% of the data is used 80% of the time. It is ideal if you wish to cache large amount of data, but for performance reasons you don't want to have a very large Map in the memory footprint.

There are two different implementations of MRU caches: single-get and multi-get. A single-get cache will only retrieve one datum at a time. A multi-get cache allows for more that one datum to be retrieved at once. This is valuable when it is expensive to load data, the application may require more than one datum at a time, and there are efficiencies to be gained in loading data in bulk. An example is a stock quoting service. A user can ask for quotes for serveral securities at once. It is more efficient to ask the quoting service for all the quotes at once than it is to get them individually.

Data Loaders

Each cache uses a data-loader to retrieve data from a data store. Data-loaders are components with custom built FunctionalImplementation classes. This is how the cache is filled with application-specific data. Data-loaders are free to get data in any way they need, e.g. direct database-access, EJB calls, Web-Services calls, etc.

Each type of cache uses a different type of component as its functional interface.

  • Total cache: org.sape.carbon.services.cache.total.TotalCacheDataLoader.
  • MRU cache: org.sape.carbon.services.cache.mru.MRUCacheDataLoader.
  • Multi-get MRU cache: org.sape.carbon.services.cache.mru.MultiGetMRUCacheDataLoader.

Data Loader Configuration

Strictly speaking, all that is required to configure a data-loader, is the type of loader it is (defined by the FunctionalInterface you choose) and the FunctionalImplementation class written by you to perform your custom data-retrieval logic. This is the minimum required information to configure any component. If there is specific configuration that your custom implementation of the data-loader requires, you must create that configuration interface and have your loader implement the Configurable lifecycle method. If there is no additional configuration required, a configuration similar to the following should suffice.

The data loader configuration is a component configuration that is linked to in the main cache configuration. The configuration can either be specified as a component reference or a nested configuration. In the case of the cache service, since a dataloader configuration is generally unique to a cache, it usually makes sense to specify it as a nested configuration.

<Configuration
  ConfigurationInterface="org.sape.carbon.core.component.ComponentConfiguration">

     <FunctionalInterface>org.sape.carbon.services.cache.total.TotalCacheDataLoader</FunctionalInterface>
     <FunctionalImplementationClass>org.sape.carbon.services.cache.test.TestTotalDataLoader</FunctionalImplementationClass>


</Configuration>

Note that the FunctionalInterface could also be org.sape.carbon.services.cache.mru.MRUCacheDataLoader or org.sape.carbon.services.cache.mru.MultiGetMRUCacheDataLoader and FunctionalImplementationClass should be the fully qualified name of the implementation of your data-loader.

Provided Data Loaders

Carbon provides the following data loader implementations for use:

Cache Configuration

Total Cache

There are two things that need to be specifed in a total-cache configuration: what kind of total cache (read-only or writable) and the name of the data- loader component. The former is specified by the FunctionalImplementation you choose. All total caches use the org.sape.carbon.services.cache.total.TotalCacheConfiguration configuration interface.

Example read-only cache configuration

<Configuration
  ConfigurationInterface="org.sape.carbon.services.cache.total.TotalCacheConfiguration">

     <FunctionalImplementationClass>org.sape.carbon.services.cache.total.ReadOnlyCache</FunctionalImplementationClass>
     <FunctionalInterface>org.sape.carbon.services.cache.Cache</FunctionalInterface>

     <DataLoader>ref:///cache/test/TotalCacheDataLoader</DataLoader>

</Configuration>

Example writable cache configuration

<Configuration
  ConfigurationInterface="org.sape.carbon.services.cache.total.TotalCacheConfiguration">

     <FunctionalImplementationClass>org.sape.carbon.services.cache.total.WritableCache</FunctionalImplementationClass>
     <FunctionalInterface>org.sape.carbon.services.cache.Cache</FunctionalInterface>

     <DataLoader>ref:///cache/test/TotalCacheDataLoader</DataLoader>

</Configuration>

MRU Cache

There are four things that must be specifed in an MRU-cache configuration: what kind of MRU cache (multi- or single-get), the name of the data- loader component, the capacity of the cache and the expiration interval of individual elements of the cache. The type of cache is specified by the ConfiguraitonInterface you choose. Single-caches use the org.sape.carbon.services.cache.mru.MRUCacheConfiguration configuration interface and multi-get caches use the org.sape.carbon.services.cache.mru.MultiGetMRUCacheConfiguration configuration interface. The capacity setting tells the cache the maximum number of entries is can have before dropping the least recently used items. The expiration interval tells the cache that items that have been in the cache longer than a certain amount of time should be reloaded regardless of when they were last accessed. If this value is -1, this function is disabled. The Expiration interval is an optional value, defaulting to -1.

Example single-get MRU cache configuration

<Configuration ConfigurationInterface="org.sape.carbon.services.cache.mru.MRUCacheConfiguration">
    <FunctionalImplementationClass>org.sape.carbon.services.cache.mru.DefaultMRUCacheImpl</FunctionalImplementationClass>
    <FunctionalInterface>org.sape.carbon.services.cache.mru.MRUCache</FunctionalInterface>

    <DataLoader>ref:///cache/test/MRUCacheMultiDataLoader</DataLoader>

    <Capacity>999</Capacity>
    <ExpirationInterval>500</ExpirationInterval>
</Configuration>

Example multi-get MRU cache configuration

<Configuration ConfigurationInterface="org.sape.carbon.services.cache.mru.MultiGetMRUCacheConfiguration">
    <FunctionalImplementationClass>org.sape.carbon.services.cache.mru.MultiGetMRUCache</FunctionalImplementationClass>
    <FunctionalInterface>org.sape.carbon.services.cache.MultiGetCache</FunctionalInterface>

    <DataLoader>ref:///cache/test/MRUCacheMultiDataLoader</DataLoader>

    <Capacity>999</Capacity>
    <ExpirationInterval>500</ExpirationInterval>
</Configuration>

Scheduled Cache Refreshes

It is often required (especially for total caches) that a cache be refreshed on a periodic basis. For this reason, all caches are schedulable components. See the Scheduling Service for details.


Copyright © 2001-2003, Sapient Corporation