How to let Spring Boot evict single entries in cache after a certain TimeToLive (TTL)

493 Views Asked by At

Update (tl;dr)

  • Single entries shall be evicted from Cache after their TTL is due.

  • Setup:

    • Java: 17
    • spring-boot.version: 3.0.x

Objective

Requests providing an ID (each) are asking for data. The data is stored in a repository - we can assume any kind of a KV-Store alike <fooId, fooData>. To speed up the repositorie's answer, the repository is wrapped by a SpringBoot-Cache.

Approach for fast answers:

  1. Ask the cache first
  2. Ask the Repository, if the cache could not provide an answer.

For the cache to stay small the entries shall be evicted.

Samples and guides like ( https://www.baeldung.com/spring-cache-tutorial ) hand suitable code for eviction ...

... BUT the entries in the Cache shall not be evicted alltogether. Each entry shall exist in the Cache for (e.g.) 30 minutes after the last request.

Sample event flow:

To say it using events:

a) Entry foo1 is requested

  • foo1 will be evicted in 30 minutes from now (-> 30)

.. 10 minuts pass (Sum: 10 min)

b) Entry foo2 is requested

  • foo2 will be evicted in 30 minutes from now (-> 40)

.. 10 minuts pass (Sum: 20 min)

c) Entry foo1 is requested (again)

  • foo1 is reset to be evicted in 30 minutes from now (-> 50)

.. 10 minuts pass (Sum: 30 min)

  • nothing happens
    • foo1 has 20 Minutes remaining until TTL is due
    • foo2 has 20 Minutes remaining until TTL is due

.. 10 minuts pass (Sum: 40 min)

d) Entry foo2 is evicted as TTL is expired

  • foo1 has 10 Minutes remaining until TTL is due

.. 10 minuts pass (Sum: 50 min)

e) Entry foo1 is evicted as TTL is expired

Is there an easy way to solve this issue without using external libraries/products?

Sample external libraries/products not to use:

  • EhCache
  • Gemfire
  • Guava

Thanks for any advice :)

1

There are 1 best solutions below

0
d.braun1991 On

Research approaches ... .. .

(i) Is there an Implementation from SpringBoot itself?

From ..

.. I could extract as of 2020:

If you are using Spring's default implementation you won't be able to set Cache TTL.
But with other providers such as EhCache, Gemfire & Guava you can do that but
only during configuring Cache Managers.

--> Sadly: NOT to the recent knowledge ... :-/


(ii) Evict everything

Of couse this is not desirable in the first place, but the cache could be evicted completely to be refilled

Downside:
  • NOT the desired option, to much loss
  • Harsh tradeoff between a large cache with many dead entries and deleting many needed entries from cache
    • Maybe the time to evict due to a huge cache is exactly a timeslot of heavy usage.

(iii) Wrap the Cache

If the entries in the Cache are 'big'/'huge', a reasonable approach would be to setup another hashmap fooTTL, which stores <ID, TTL>-pairs.

Then a fixed delayed/scheduled function loops the fooTTL and evicts due entries from the cache.

Downside:
  • Cumbersome and not less applicable for small <Key, Value> pairs within the main cache.

(iv) Reevaluate the use of external libraries/products

Maybe the as the use of external libraries/products offers a better set of functions or a less cumbersome implementation.

Downside:
  • NOT in the initial scope.

(v) Replace the Cache

In the first place the SpringBoot Simple Cache is a "Key-Value"-Pair HashMap<K,V>. Maybe it is even a ConcurentHashMap<K,V>.

Anyways, a handcrafted class using a ConcurentHashMap<K,V> might be your solution, as everything stays in memory.

The functions from the SpringBoot Simple Cache are pretty simple.

- add(K,V)
- update(K,V)
- get(K)     -->  V
- exist(K)   -->  Bool
- evict(K)

This interface could be extended by:

- deleteByTtl

The key stays equivalent to the implementation given by Spring. Additionally the new value field wrappes two fields:

  • the value used previously for the SpringBoot Simple Cache
  • a ttl timestamp

Finally the deleteByTtl-funtion can be @Sceduled to iterate over all entries evicting only those with an expired ttl.

Downside:
  • Does not use the SpringBoot-@Cachable notation.

Still approach (v) remains straight foreward regarding the persued functionality.