Issue
When profiling an application it came up that Redis is impacting the execution times because there are many sleeps in threads. I need to implement two levels of cache or think about solution of this problem.
I would like to have two levels of caches:
The solution that I came up with is:
- Create two CacheManagers (CaffeineCacheManager and RedisCacheManager),
- Initialize same caches for each cache manager,
- Use annotation @Caching with cacheable={} to use two caches,
@Caching(cacheable = {
@Cacheable(cacheNames = CacheConfiguration.HELLO_WORLD),
@Cacheable(cacheNames = CacheConfiguration.HELLO_WORLD, cacheManager = "cacheManagerRedis")
})
public String generate(String name)
{
log.info(" Cached method call...");
return helloWorldService.generate(name);
}
The structure of classes is similar to: CachedService (annotations here) -> NonCachedService
The problem I am facing:
I wanted to have it working in flow (yes - works/n - not working):
- [ y ] data is fetched and then cached to both caches Redis and local - this works
- [ y ] if data exists in local cache do not move it to redis - this works
- [ y ] if any of caches contains the data it will be fetched from cache
- [ n ] if data exists in Redis, move it to local - this does not work
Modification of @Caching annotation to have put={} where it would put values into local cache is making whole cache not working.
@Caching(cacheable = {
@Cacheable(cacheNames = CacheConfiguration.HELLO_WORLD),
@Cacheable(cacheNames = CacheConfiguration.HELLO_WORLD, cacheManager = "cacheManagerRedis")
}, put = {
@CachePut(cacheNames = CacheConfiguration.HELLO_WORLD),
})
public String generate(String name)
{
log.info(" Cached method call...");
return helloWorldService.generate(name);
}
- Do you know any spring-ready solutions to work with two levels of cache?
- I've read about local caching with Redis but it does not mean anything similar to my case (it's just the standard redis use case),
- I am left only with double-layered services structure to achieve this goal? Similar to CachedLocal -> CachedRedis -> NonCached
Solution
For anyone else looking for this, I was able to implement the CacheInterceptor suggested by Ankit.
Example:
public class RedisMultiCacheInterceptor extends CacheInterceptor {
@Autowired
private CacheManager caffeineCacheManager;
@Override
protected Cache.ValueWrapper doGet(Cache cache, Object key) {
//Get item from cache
var superGetResult = super.doGet(cache, key);
if (superGetResult == null) {
return superGetResult;
}
//If retrieved from Redis, check if it's missing from caffeine on local and add it
if (cache.getClass() == RedisCache.class) {
var caffeineCache = caffeineCacheManager.getCache(cache.getName());
if (caffeineCache != null) {
caffeineCache.putIfAbsent(key, superGetResult.get());
}
}
return superGetResult;
}
}
Answered By - mslissap
Answer Checked By - Senaida (JavaFixing Volunteer)