38/68

# 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>