28 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 will not need 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 28-1 shows the implementation of a simple custom eviction policy:

Example 28-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; should be 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 (i.e. the key/value) need
            // to be examined, invoke convertEntry(entry) in case
            // the entry needs to 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);
 
            // In this example, we're simply iterating and removing items
            // from the cache until we are 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 will deserialize 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 28-2 illustrates a Coherence cache configuration file (coherence-cache-config.xml) with an eviction policy:

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

<?xml version="1.0"?>
 
<!DOCTYPE cache-config SYSTEM "cache-config.dtd">
 
<cache-config>
  <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 will be 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>