3

I am trying to setup L1 + L2 caching strategy to use with @Cacheable annotation. My objective is

  1. Configure Caffeine Cache
  2. Configure Redis Cache
  3. Look up item in Caffeine Cache, if found return, else step 4
  4. Look up item in Redis Cache, if found return and cache in caffeine, else step 5
  5. Use real service to return the result.

I know this is not supported out of the box, but i have been trying to read the documentation on how to go about wiring up such a solution.

My current solution is to wrap my actual service is a RedisBackedService which has the redisCacheManager on the cacheable annotation which in turn is wrapped in a CaffeineBackedService which has a caffeineCacheManager on the cacheable annotation. Needless to say it seems redundant.

Any pointers would be helpful.

Adil F
  • 447
  • 1
  • 5
  • 14
  • What do you mean by L1 cache? In which context? You can easily create a `Cache` instance that delegate to caffeine and fallback to redis if not found. The CacheManager interface is two methods and very easy as well. – Stephane Nicoll Dec 16 '18 at 08:55
  • Level 1 ( local ) and Level 2 ( remote ). I could manually do it but then I loose the power of cacheable annotation. are you saying I should inject a custom cache ( That checks redis and caffeine) into a custom manager? – Adil F Dec 17 '18 at 08:36
  • Yes. It's not that hard really and you can still benefit from everything `@Cacheable` has to offer. You don't even need a `CacheManager` implementation, you can just register your custom caches in a `SimpleCacheManager` if you know them upfront. – Stephane Nicoll Dec 17 '18 at 09:38
  • Thank you. I will try it out. Appreciate the guidance – Adil F Dec 17 '18 at 17:41

1 Answers1

4

So to conclude here and provide an answer based on the comments, this is not a feature of the cache abstraction but the SPI of the cache abstraction is simple enough for you to implement something yourself.

public class FallbackCache implements Cache {

  private final Cache primaryCache;
  private final Cache fallbackCache;

  FallbackCache(Cache primaryCache, Cache fallbackCache) { ... }

  public ValueWrapper get(Object key) {
    ValueWrapper wrapper = primaryCache.get(key);
    if (wrapper != null) {
      return wrapper;
    }
    return fallbackCache.get(key);
  }

  // other methods

}

Some methods like the native cache accessor can be a bit tricky for this use case. I'd return the primary cache and hide the fact that there is a fallback from the caller.

If you know your caches upfront, then you can create them and wrap them in a SimpleCacheManager. If you need to create them on the fly, the CacheManager API requires you to implement two simple methods.

Stephane Nicoll
  • 31,977
  • 9
  • 97
  • 89