Creating and Using Coherence Caches

Overview

The simplest and most flexible way to create caches in Coherence is to use the cache configuration descriptor to define attributes and names for your application's or cluster's caches, and to instantiate the caches in your application code referring to them by name that matches the names or patterns as defined in the descriptor.

This approach to configuring and using Coherence caches has a number of very important benefits. It separates the cache initialization and access logic for the cache in your application from its attributes and characteristics. This way your code is written in a way that is independent of the cache type that will be utilized in your application deployment and changing the characteristics of each cache (such as cache type, cache eviction policy, and cache type-specific attributes, etc.) can be done without making any changes to the code whatsoever. It allows you to create multiple configurations for the same set of named caches and to instruct your application to use the appropriate configuration at deployment time by specifying the descriptor to use in the java command line when the node JVM is started.

Creating a cache in your application.

To instantiate a cache in your application code, you need to:

  1. Make sure that coherence.jar and tangosol.jar are in your classpath.
  2. Use CacheFactory.getCache() to access the cache in your code.

Your code will look similar to the following:

import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;

...

NamedCache cache = CacheFactory.getCache("VirtualCache");

Now you can retrieve and store objects in the cache, using the NamedCache API, which extends the standard java.util.Map interface, adding a number of additional capabilities that provide concurrency control (ConcurrentMap interface), ability to listen for cache changes (ObservableMap interface) and ability to query the cache (QueryMap interface).

The following is an example of typical cache operations:

// simple retrieve and update cycle
String key = "key";

// retrieve the object
MyValue value = (MyValue) cache.get(key);

// Use and modify the object
// ...

// put the new value back
cache.put(key, value);

Configuring the caches

The cache attributes and settings are defined in the cache configuration descriptor. Cache attributes determine the cache type (what means and resources the cache will use for storing, distributing and synchronizing the cached data) and cache policies (what happens to the objects in the cache based on cache size, object longevity and other parameters).

The structure of the cache configuration descriptor (described in detail by the cache-config.dtd included in the coherence.jar) consists of two primary sections: caching-schemes section and caching-scheme-mapping section.

The caching-schemes section is where the attributes of a cache or a set of caches get defined. The caching schemes can be of a number of types, each with its own set of attributes. The caching schemes can be defined completely from scratch, or can incorporate attributes of other existing caching schemes, referring to them by their scheme-names (using a scheme-ref element) and optionally overriding some of their attributes to create new caching schemes. This flexibility enables you to create caching scheme structures that are easy to maintain, foster reuse and are very flexible.

The caching-scheme-mapping section is where the specific cache name or a naming pattern is attached to the cache scheme that defines the cache configuration to use for the cache that matches the name or the naming pattern.

So if we would like to define the cache descriptor for the cache we mentioned in the previous section (VirtualCache), it may look something like the following:

<?xml version="1.0"?>
<!DOCTYPE cache-config SYSTEM "cache-config.dtd">

<cache-config>
    <caching-scheme-mapping>
    <!--
    Caches with any name will be created as default replicated.
    -->
    <cache-mapping>
        <cache-name>*</cache-name>
        <scheme-name>default-replicated</scheme-name>
    </cache-mapping>
    </caching-scheme-mapping>

    <caching-schemes>
        <!--
        Default Replicated caching scheme.
        -->
        <replicated-scheme>
            <scheme-name>default-replicated</scheme-name>
            <service-name>ReplicatedCache</service-name>
            <backing-map-scheme>
                <class-scheme>
                    <scheme-ref>default-backing-map</scheme-ref>
                </class-scheme>
            </backing-map-scheme>
        </replicated-scheme>

        <!--
        Default backing map scheme definition used by all
        The caches that do not require any eviction policies
        -->
        <class-scheme>
            <scheme-name>default-backing-map</scheme-name>
            <class-name>com.tangosol.util.SafeHashMap</class-name>
        </class-scheme>

    </caching-schemes>
</cache-config>

The above cache configuration descriptor specifies that all caches will be created (including our VirtualCache cache) utilizing the default-replicated caching scheme. It defines the default-replicated caching scheme as a replicated-scheme, utilizing a service named ReplicatedCache and utilizing the backing map named default-backing-map, which is defined as a class com.tangosol.util.SafeHashMap (the default backing map storage that Coherence uses when no eviction policies are required).

Then, at a later point, let's say we decide that, since the number of entries that our cache is holding is too large and updates to the objects too frequent to use a replicated cache, we want our VirtualCache cache to become a distributed cache instead (while keeping all other caches replicated). To accommodate these new circumstances, we can change the cache configuration by adding the following cache-scheme definition for the distributed cache to the caching-schemes section:

<!--
Default Distributed caching scheme.
-->
<distributed-scheme>
    <scheme-name>default-distributed</scheme-name>
    <service-name>DistributedCache</service-name>
    <backing-map-scheme>
        <class-scheme>
            <scheme-ref>default-backing-map</scheme-ref>
        </class-scheme>
    </backing-map-scheme>
</distributed-scheme>

and then mapping the VirtualCache cache to it in the caching-schemes-mapping section:

<cache-mapping>
    <cache-name>VirtualCache</cache-name>
    <scheme-name>default-distributed</scheme-name>
</cache-mapping>

The resulting cache definition descriptor will look as follows:

<?xml version="1.0"?>
<!DOCTYPE cache-config SYSTEM "cache-config.dtd">

<cache-config>
    <caching-scheme-mapping>
        <!--
        Caches with any name will be created as default replicated.
        -->
        <cache-mapping>
            <cache-name>*</cache-name>
            <scheme-name>default-replicated</scheme-name>
        </cache-mapping>
        <cache-mapping>
            <cache-name>VirtualCache</cache-name>
            <scheme-name>default-distributed</scheme-name>
        </cache-mapping>
    </caching-scheme-mapping>

    <caching-schemes>
        <!--
        Default Replicated caching scheme.
        -->
        <replicated-scheme>
            <scheme-name>default-replicated</scheme-name>
            <service-name>ReplicatedCache</service-name>

            <backing-map-scheme>
                <class-scheme>
                    <scheme-ref>default-backing-map</scheme-ref>
                </class-scheme>
            </backing-map-scheme>
        </replicated-scheme>

        <!--
        Default Distributed caching scheme.
        -->
        <distributed-scheme>
            <scheme-name>default-distributed</scheme-name>
            <service-name>DistributedCache</service-name>

            <backing-map-scheme>
                <class-scheme>
                    <scheme-ref>default-backing-map</scheme-ref>
                </class-scheme>
            </backing-map-scheme>
        </distributed-scheme>

        <!--
        Default backing map scheme definition used by all
        The caches that do not require any eviction policies
        -->
        <class-scheme>
            <scheme-name>default-backing-map</scheme-name>

            <class-name>com.tangosol.util.SafeHashMap</class-name>
        </class-scheme>

    </caching-schemes>
</cache-config>

Once we revise and deploy the descriptor and restart the cluster, the VirtualCache cache will be a distributed cache instead of replicated, all without any changes to the code we wrote.

Cache Configuration Descriptor location

A few words about how to instruct Coherence where to find the cache configuration descriptor. Without specifying anything in the command java command line, Coherence will attempt to use the cache configuration descriptor named coherence-cache-config.xml that it finds in the classpath. Since Coherence ships with this file packaged into the coherence.jar, unless you place another file with the same name in the classpath location preceding coherence.jar, that is the one that Coherence will use. You can tell Coherence to use a different default descriptor by using the -Dtangosol.coherence.cacheconfig java command line property as follows:

java -Dtangosol.coherence.cacheconfig=/cfg/my-config.xml AppServer

The above command instructs Coherence to use my-config.xml file in /cfg directory as the default cache configuration descriptor. As you can see, this capability can give you the flexibility to modify the cache configurations of your applications without making any changes to the application code and by simply specifying different cache configuration descriptors at application deployment or start-up.

Putting it all together: your first Coherence cache example

Let's try walking through creating a working example cache using the caches and the cache configuration descriptor we described in the previous section. The easiest way to initially do that is to use the Coherence command line application. A couple of general comments regarding this example before we get started:

Setting up your test environment

To set up the test environment, you will need install Coherence by unzipping the software distribution in the desired location on one or more machines.

The coherence/examples directory of the software contains the following examples that we will be making use of in this exercise:

To deploy and run it, you need to execute the following java command line (from the tangosol directory):

The examples/jsp/explore/SimpleCacheExplorer.jsp is the JSP file that can be used with your favorite application server:

Modifying the cache configuration

Once you are comfortable with the test setup, let's change the cache configuration and test our changes, using this simple test harness. Please remember that after each cache configuration change all the cluster members need to be shut down and then restarted (whether you are using application server instances or just plain java JVMs). All our test are configured to use coherence/examples/config/explore-config.xml, so this the file that needs to be edited to make cache configuration changes.

Let's make the first change we described previously, changing the VirtualCache to be a distributed cache by adding the following (bolded) sections:

<?xml version="1.0"?>
<!DOCTYPE cache-config SYSTEM "cache-config.dtd">

<cache-config>
    <caching-scheme-mapping>
        <!--
        Caches with any name will be created as default replicated.
        -->
        <cache-mapping>
            <cache-name>*</cache-name>
            <scheme-name>default-replicated</scheme-name>
        </cache-mapping>
       <cache-mapping>
            <cache-name>VirtualCache</cache-name>
            <scheme-name>default-distributed</scheme-name>
        </cache-mapping>
    </caching-scheme-mapping>

    <caching-schemes>
        <!--
        Default Replicated caching scheme.
        -->
        <replicated-scheme>
            <scheme-name>default-replicated</scheme-name>
            <service-name>ReplicatedCache</service-name>

            <backing-map-scheme>
                <class-scheme>
                    <scheme-ref>default-backing-map</scheme-ref>
                </class-scheme>
            </backing-map-scheme>
        </replicated-scheme>

        <!--
        Default Distributed caching scheme.
        -->
        <distributed-scheme>
            <scheme-name>default-distributed</scheme-name>
            <service-name>DistributedCache</service-name>

            <backing-map-scheme>
                <class-scheme>
                    <scheme-ref>default-backing-map</scheme-ref>
                </class-scheme>
            </backing-map-scheme>
        </distributed-scheme>

        <!--
        Default backing map scheme definition used by all
        The caches that do not require any eviction policies
        -->
        <class-scheme>
            <scheme-name>default-backing-map</scheme-name>

            <class-name>com.tangosol.util.SafeHashMap</class-name>
        </class-scheme>

    </caching-schemes>
</cache-config>

After the changes are saved, the test intances are restarted and you have had a chance to do some test data entry to see how the cache behaves, you should see the following in the cache configuration section of the tests:

Now let's add an eviction policy for our default distributed cache, limiting it's size to 5 entries (per node) and setting the entry expiry to 60 seconds with an LRU eviction policy. To do that we need to make the following (bolded) changes to our descriptor:

<?xml version="1.0"?>
<!DOCTYPE cache-config SYSTEM "cache-config.dtd">
<cache-config>
    <caching-scheme-mapping>
        <!--
        Caches with any name will be created as default replicated.
        -->
        <cache-mapping>
            <cache-name>*</cache-name>
            <scheme-name>default-replicated</scheme-name>
        </cache-mapping>
        <cache-mapping>
            <cache-name>VirtualCache</cache-name>
            <scheme-name>default-distributed</scheme-name>
        </cache-mapping>
    </caching-scheme-mapping>
    <caching-schemes>
        <!--
        Default Replicated caching scheme.
        -->
        <replicated-scheme>
            <scheme-name>default-replicated</scheme-name>
            <service-name>ReplicatedCache</service-name>
            <backing-map-scheme>
                <class-scheme>
                    <scheme-ref>default-backing-map</scheme-ref>
                </class-scheme>
            </backing-map-scheme>
        </replicated-scheme>
        <!-- 
        Default Distributed caching scheme.
        -->
        <distributed-scheme>
            <scheme-name>default-distributed</scheme-name>
            <service-name>DistributedCache</service-name>
            <backing-map-scheme>
              <local-scheme>
                <scheme-ref>default-eviction</scheme-ref>
                <eviction-policy>LRU</eviction-policy>
                <high-units>5</high-units>
                <expiry-delay>60</expiry-delay>
              </local-scheme>
            </backing-map-scheme>
        </distributed-scheme>
        <!-- 
        Default backing map scheme definition used by all
        The caches that do not require any eviction policies
        -->
        <class-scheme>
            <scheme-name>default-backing-map</scheme-name>
            <class-name>com.tangosol.util.SafeHashMap</class-name>
        </class-scheme>
        <!--
        Default eviction policy scheme.
        -->
        <local-scheme>
          <scheme-name>default-eviction</scheme-name>
          <eviction-policy>HYBRID</eviction-policy>
          <high-units>0</high-units>
          <expiry-delay>3600</expiry-delay>
        </local-scheme>
    </caching-schemes>
</cache-config>

Please note that we defined a general purpose local-scheme 'default-eviction' (with no size limit, 5 minute expiry and a HYBRID eviction policy) and then used it by reference (using scheme-ref) for our default-distributed scheme definition, overriding it's configuration settings to match our requirements.

After the changes are saved, the test intances are restarted and you have had a chance to do some test data entry to see how the cache behaves, you should see the following in the cache configuration section of the tests:

Try doing some puts and gets, carefully noting the time you last updated the specific entries. You should see that the number of entries does not exceed 5 entries per node (so if you have 2 nodes running the number of entries should not exceed 10, for 3 nodes - 15, and so on) and entries either expire after they have not been updated for 60 seconds, or when you add the 6th entry (with the least recently touched entries being 'evicted' from the cache first. (Hint: use the 'keys' command in the SimpleCacheExplorer.java to see the list of keys in the cache.)

These examples show you the general approach to modifying the cache configurations without making any code changes (as you no doubt noticed we did not touch our test application's code). Please refer to the cache-config.dtd, which can be found in the coherence.jar for full details on the available cache configuration descriptor settings and the explanation of their meaning and possible settings.


Attachments:
Example-SimpleCacheExplorerJSP-1.gif (image/gif)
Example-SimpleCacheExplorerJSP-2.gif (image/gif)
Example-SimpleCacheExplorerJSP-3.gif (image/gif)