Introduction

As conveyed in the Data Freshness and Expiration documentation, this is one of the key aspects of caching. In Ehcache 3 this is addressed with the Expiry interface and its use in controlling the age of cache mappings.

Configuration

Expiry is configured at the cache level, in Java or in XML:

CacheConfiguration<Long, String> cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
        ResourcePoolsBuilder.heap(100)) (1)
    .withExpiry(Expirations.timeToLiveExpiration(Duration.of(20, TimeUnit.SECONDS))) (2)
    .build();
1 Expiry is configured at the cache level, so start by defining a cache configuration,
2 then add to it an Expiry, here using the predefined time-to-live one, configured with the required Duration.
<cache alias="foo">
  <expiry>
    <ttl unit="seconds">20</ttl> (1)
  </expiry>
  <heap>100</heap>
</cache>
1 At the cache level, using the predefined time-to-live again.

Both Java and XML offer direct support for three types of expiry:

no expiry

this means cache mappings will never expire,

time-to-live

this means cache mappings will expire after a fixed duration following their creation,

time-to-idle

this means cache mappings will expire after a fixed duration following the time they were last accessed.

For Java, see org.ehcache.expiry.Expirations and the XSD for XML.

Read on to implement your own expiration scheme.

Custom expiry

Supporting your own expiration scheme simply means implementing the Expiry interface:

/**
 * A policy object that governs expiration for mappings in a {@link org.ehcache.Cache Cache}.
 * <P>
 *   Previous values are not accessible directly but are rather available through a {@link ValueSupplier value supplier}
 *   to indicate that access can require computation (such as deserialization).
 * </P>
 * <P>
 * NOTE: Some cache configurations (eg. caches with eventual consistency) may use local (ie. non-consistent) state
 * to decide whether to call {@link #getExpiryForUpdate(Object, ValueSupplier, Object)}  vs.
 * {@link #getExpiryForCreation(Object, Object)}. For these cache configurations it is advised to return the same
 * value for both of these methods
 * </P>
 * <P>
 * See {@link Expirations} for helper methods to create common {@code Expiry} instances.
 * </P>
 *
 * @param <K> the key type for the cache
 * @param <V> the value type for the cache
 *
 * @see Expirations
 */
public interface Expiry<K, V> {

  /**
   * Returns the lifetime of an entry when it is initially added to a {@link org.ehcache.Cache Cache}.
   * <P>
   *   This method must not return {@code null}.
   * </P>
   * <P>
   *   Exceptions thrown from this method will be swallowed and result in the expiry duration being
   *   {@link Duration#ZERO ZERO}.
   * </P>
   *
   * @param key the key of the newly added entry
   * @param value the value of the newly added entry
   * @return a non-null {@link Duration}
   */
  Duration getExpiryForCreation(K key, V value);

  /**
   * Returns the expiration {@link Duration} (relative to the current time) when an existing entry is accessed from a
   * {@link org.ehcache.Cache Cache}.
   * <P>
   *   Returning {@code null} indicates that the expiration time remains unchanged.
   * </P>
   * <P>
   *   Exceptions thrown from this method will be swallowed and result in the expiry duration being
   *   {@link Duration#ZERO ZERO}.
   * </P>
   *
   * @param key the key of the accessed entry
   * @param value a value supplier for the accessed entry
   * @return an expiration {@code Duration}, {@code null} means unchanged
   */
  Duration getExpiryForAccess(K key, ValueSupplier<? extends V> value);


  /**
   * Returns the expiration {@link Duration} (relative to the current time) when an existing entry is updated in a
   * {@link org.ehcache.Cache Cache}.
   * <P>
   *   Returning {@code null} indicates that the expiration time remains unchanged.
   * </P>
   * <P>
   *   Exceptions thrown from this method will be swallowed and result in the expiry duration being
   *   {@link Duration#ZERO ZERO}.
   * </P>
   *
   * @param key the key of the updated entry
   * @param oldValue a value supplier for the previous value of the entry
   * @param newValue the new value of the entry
   * @return an expiration {@code Duration}, {@code null} means unchanged
   */
  Duration getExpiryForUpdate(K key, ValueSupplier<? extends V> oldValue, V newValue);

}

The main points to remember on the return value from these methods:

some Duration

indicates that the mapping will expire after that duration,

Duration.ZERO

indicates that the mapping is immediately expired,

Duration.INFINITE

indicates that the mapping will never expire,

null Duration

indicates that the previous expiration time is to be left unchanged, illegal at mapping creation time.

Note that you can access the details of the mapping, thus providing expiration times that are different per mapping.

Also when used from XML, Ehcache expects your expiry implementation to have a no-arg constructor.

Once you have implemented your own expiry, simply configure it.

In Java:

CacheConfiguration<Long, String> cacheConfiguration = CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
        ResourcePoolsBuilder.heap(100))
    .withExpiry(new CustomExpiry()) (1)
    .build();
1 Simply pass your custom expiry instance into the cache builder.

In XML:

<cache alias="foo">
  <expiry>
    <class>com.pany.ehcache.MyExpiry</class> (1)
  </expiry>
  <heap>100</heap>
</cache>
1 Simply pass the fully qualified class name of your custom expiry.

There is a migration guide in place to demonstrate how to migrate the Ehcache 2.x per mapping expiry code to Ehcache 3.x.