31 Specifying a Custom Eviction Policy

The LocalCache class is used for size-limited caches. It is used both for caching on-heap objects (as in a local cache or the front portion of a near cache) and as the backing map for a partitioned cache. Applications can provide custom eviction policies for use with a LocalCache.

Note that Coherence's default eviction policy is very effective for most workloads; the majority of applications do not have to provide a custom policy. Generally, it is best to restrict the use of eviction policies to scenarios where the evicted data is present in a backing system (that is, the back portion of a near cache or a database). Eviction should be treated as a physical operation (freeing memory) and not a logical operation (deleting an entity).

Example 31-1 shows the implementation of a simple custom eviction policy:

Example 31-1 Implementing a Custom Eviction Policy

package com.tangosol.examples.eviction;
 
import com.tangosol.net.cache.AbstractEvictionPolicy;
import com.tangosol.net.cache.ConfigurableCacheMap;
import com.tangosol.net.cache.LocalCache;
import com.tangosol.net.BackingMapManagerContext;
import com.tangosol.util.ConverterCollections;
import java.util.Iterator;
import java.util.Map;

/**
 * Custom eviction policy that evicts items randomly (or more specifically,
 * based on the natural order provided by the map's iterator.)
 * This example may be used in cases where fast eviction is required
 * with as little processing as possible.
 */
public class SimpleEvictionPolicy
        extends AbstractEvictionPolicy
    {
    /**
     * Default constructor; typically used with local caches or the front
     * parts of near caches.
     */
    public SimpleEvictionPolicy()
        {
        }
 
    /**
     * Constructor that accepts {@link BackingMapManagerContext}; should
     * be used with partitioned cache backing maps.
     *
     * @param ctx  backing map context
     */
    public SimpleEvictionPolicy(BackingMapManagerContext ctx)
        {
        m_ctx = ctx;
        }
 
    /**
     * {@inheritDoc}
     */
    public void entryUpdated(ConfigurableCacheMap.Entry entry)
        {
        }
 
    /**
     * {@inheritDoc}
     */
    public void entryTouched(ConfigurableCacheMap.Entry entry)
        {
        }
 
    /**
     * {@inheritDoc}
     */
    public void requestEviction(int cMaximum)
        {
        ConfigurableCacheMap cache = getCache();
        Iterator iter  = cache.entrySet().iterator();
 
        for (int i = 0, c = cache.getUnits() - cMaximum; i < c && iter.hasNext();
           i++)
            {
            ConfigurableCacheMap.Entry entry  = (ConfigurableCacheMap.Entry)
               iter.next();
            StringBuffer               buffer = new StringBuffer();
 
            // If the contents of the entry (for example the key/value) need
            // to be examined, invoke convertEntry(entry) in case
            // the entry must be deserialized
            Map.Entry convertedEntry = convertEntry(entry);
            buffer.append("Entry: ").append(convertedEntry);
 
            // Here's how to get metadata about creation/last touched
            // timestamps for entries.  This information might be used
            // in determining what gets evicted.
            if (entry instanceof LocalCache.Entry)
                {
                buffer.append(", create millis=");
                buffer.append(((LocalCache.Entry) entry).getCreatedMillis());
                }
            buffer.append(", last touch millis=");
            buffer.append(entry.getLastTouchMillis());
 
            // This output is for illustrative purposes; this may generate
            // excessive output in a production system
            System.out.println(buffer);
 
            // iterate and remove items
            // from the cache until below the maximum.  Note that
            // the non converted entry key is passed to the evict method
            cache.evict(entry.getKey());
            }
        }
 
    /**
     * If a {@link BackingMapManagerContext} is configured, wrap the
     * Entry with {@link ConverterCollections.ConverterEntry} in order
     * to deserialize the entry.
     *
     * @see ConverterCollections.ConverterEntry
     * @see BackingMapManagerContext
     *
     * @param entry  entry to convert if necessary
     *
     * @return an entry that deserializes its key and value if necessary
     */
    protected Map.Entry convertEntry(Map.Entry entry)
        {
        BackingMapManagerContext ctx = m_ctx;
        return ctx == null ? entry :
                new ConverterCollections.ConverterEntry(entry,
                        ctx.getKeyFromInternalConverter(),
                        ctx.getValueFromInternalConverter(),
                        ctx.getValueToInternalConverter());
        }
 
    private BackingMapManagerContext m_ctx;
    }

Example 31-2 illustrates a Coherence cache configuration file (coherence-cache-config.xml) with an eviction policy:

Example 31-2 Custom Eviction Policy in a coherence-cache-config.xml File

<?xml version="1.0"?>
 
<cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"
   xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-cache-config
   coherence-cache-config.xsd">
   <caching-scheme-mapping>
      <cache-mapping>
         <cache-name>*</cache-name>
         <scheme-name>example-near</scheme-name>
      </cache-mapping>
   </caching-scheme-mapping>
 
   <caching-schemes>
      <near-scheme>
         <scheme-name>example-near</scheme-name>
         <front-scheme>
            <local-scheme>
               <eviction-policy>
                  <class-scheme>
                     <class-name>
                        com.tangosol.examples.eviction.SimpleEvictionPolicy
                     </class-name>
                  </class-scheme>
               </eviction-policy>
               <high-units>1000</high-units>
            </local-scheme>
         </front-scheme>
         <back-scheme>
            <distributed-scheme>
               <scheme-ref>example-distributed</scheme-ref>
            </distributed-scheme>
         </back-scheme>
         <invalidation-strategy>all</invalidation-strategy>
         <autostart>true</autostart>
      </near-scheme>
 
      <distributed-scheme>
         <scheme-name>example-distributed</scheme-name>
         <service-name>DistributedCache</service-name>
         <backing-map-scheme>
            <local-scheme>
               <eviction-policy>
                  <class-scheme>
                     <class-name>
                        com.tangosol.examples.eviction.SimpleEvictionPolicy
                     </class-name>
                     <init-params>
              <!--
               Passing the BackingMapManagerContext to the eviction policy;
               this is required for deserializing entries
               -->
                        <init-param>
                           <param-type>
                           com.tangosol.net.BackingMapManagerContext</param-type>
                           <param-value>{manager-context}</param-value>
                        </init-param>
                     </init-params>
                  </class-scheme>
               </eviction-policy>
               <high-units>20m</high-units>
               <unit-calculator>binary</unit-calculator>
            </local-scheme>
         </backing-map-scheme>
         <autostart>true</autostart>
      </distributed-scheme>
   </caching-schemes>
</cache-config>