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 ExpiryPolicy 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(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(20))) (2)
    .build();
1 Expiry policy is configured at the cache level, so start by defining a cache configuration,
2 then add to it an ExpiryPolicy, here using the predefined time-to-live one, configured with the required Duration.
<cache alias="withExpiry">
  <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.config.builders.ExpiryPolicyBuilder and the XSD for XML.

Read on to implement your own expiration scheme.

Custom expiry

Supporting your own expiration scheme simply means implementing the ExpiryPolicy 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 value {@code Supplier}
 * to indicate that access can require computation (such as deserialization).
 * <p>
 * {@link java.time.Duration#isNegative() Negative durations} are not supported, expiry policy implementation returning such a
 * duration will result in immediate expiry, as if the duration was {@link java.time.Duration#ZERO zero}.
 * <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, Supplier, Object)}  vs.
 * {@link #getExpiryForCreation(Object, Object)}. For these cache configurations it is advised to return the same
 * value for both of these methods
 *
 * @param <K> the key type for the cache
 * @param <V> the value type for the cache
 *
 */
public interface ExpiryPolicy<K, V> {

  /**
   * A {@link Duration duration} that represents an infinite time.
   */
  Duration INFINITE = Duration.ofNanos(Long.MAX_VALUE);

  /**
   * An {@code ExpiryPolicy} that represents a no expiration policy
   */
  ExpiryPolicy<Object, Object> NO_EXPIRY = new ExpiryPolicy<Object, Object>() {
    @Override
    public Duration getExpiryForCreation(Object key, Object value) {
      return INFINITE;
    }

    @Override
    public Duration getExpiryForAccess(Object key, Supplier<?> value) {
      return null;
    }

    @Override
    public Duration getExpiryForUpdate(Object key, Supplier<?> oldValue, Object newValue) {
      return null;
    }
  };

  /**
   * 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>
   * Exceptions thrown from this method will be swallowed and result in the expiry duration being
   * {@link Duration#ZERO ZERO}.
   *
   * @param key the key of the newly added entry
   * @param value the value of the newly added entry
   * @return a non-null {@code Duration}
   */
  Duration getExpiryForCreation(K key, V value);

  /**
   * Returns the expiration {@link Duration 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>
   * Exceptions thrown from this method will be swallowed and result in the expiry duration being
   * {@link Duration#ZERO ZERO}.
   *
   * @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, Supplier<? extends V> value);


  /**
   * Returns the expiration {@link Duration 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>
   * Exceptions thrown from this method will be swallowed and result in the expiry duration being
   * {@link Duration#ZERO ZERO}.
   *
   * @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, Supplier<? 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 policy, 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="withCustomExpiry">
  <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.