Class MultiLevelCache

java.lang.Object
org.springframework.cache.support.AbstractValueAdaptingCache
org.springframework.data.redis.cache.RedisCache
io.github.suppie.spring.cache.MultiLevelCache
All Implemented Interfaces:
org.springframework.cache.Cache

public class MultiLevelCache extends org.springframework.data.redis.cache.RedisCache
Multi-level cache implementation

Main goals are:

  • Bypass calls to Redis to speed up retrieval entries
  • Provide fault tolerance means if Redis is unavailable without loss in functionality

WARNING: When dealing with local cache we do partial key conversion using RedisCache.convertKey(Object) for consistency and retrieval of correct String

  • Nested Class Summary

    Nested classes/interfaces inherited from interface org.springframework.cache.Cache

    org.springframework.cache.Cache.ValueRetrievalException, org.springframework.cache.Cache.ValueWrapper
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    protected final io.github.resilience4j.circuitbreaker.CircuitBreaker
     
    protected final com.github.benmanes.caffeine.cache.Cache<Object,Object>
     
    protected final com.github.benmanes.caffeine.cache.Cache<Object,ReentrantLock>
     
     
  • Constructor Summary

    Constructors
    Constructor
    Description
    MultiLevelCache(String name, MultiLevelCacheConfigurationProperties properties, org.springframework.data.redis.cache.RedisCacheWriter redisCacheWriter, org.springframework.data.redis.core.RedisTemplate<Object,Object> redisTemplate, com.github.benmanes.caffeine.cache.Cache<Object,Object> localCache, io.github.resilience4j.circuitbreaker.CircuitBreaker cacheCircuitBreaker)
    Creates a new instance of MultiLevelCache.
    MultiLevelCache(String name, MultiLevelCacheConfigurationProperties properties, org.springframework.data.redis.core.RedisTemplate<Object,Object> redisTemplate, com.github.benmanes.caffeine.cache.Cache<Object,Object> localCache, io.github.resilience4j.circuitbreaker.CircuitBreaker cacheCircuitBreaker)
    Initializes a new instance of the MultiLevelCache class with the given parameters.
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    Clear the cache through removing all mappings.
    void
    Evict the mapping for this key from this cache if it is present.
    boolean
    Evict the mapping for this key from this cache if it is present, expecting the key to be immediately invisible for later lookups.
    <T> T
    get(Object key, Callable<T> valueLoader)
    Return the value to which this cache maps the specified key, obtaining that value from valueLoader if necessary.
    boolean
    Invalidate the cache through removing all mappings, expecting all entries to be immediately invisible for later lookups.
    protected Object
    Perform an actual lookup in the underlying store.
    void
    put(Object key, Object value)
    Associate the specified value with the specified key in this cache.
    org.springframework.cache.Cache.ValueWrapper
    putIfAbsent(Object key, Object value)
    Atomically associate the specified value with the specified key in this cache if it is not set already.

    Methods inherited from class org.springframework.data.redis.cache.RedisCache

    clear, clearStatistics, convertKey, createCacheKey, deserializeCacheValue, getCacheConfiguration, getCacheWriter, getConversionService, getName, getNativeCache, getStatistics, loadCacheValue, preProcessCacheValue, retrieve, retrieve, serializeCacheKey, serializeCacheValue

    Methods inherited from class org.springframework.cache.support.AbstractValueAdaptingCache

    fromStoreValue, get, get, isAllowNullValues, toStoreValue, toValueWrapper

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • properties

      protected final MultiLevelCacheConfigurationProperties properties
    • localCache

      protected final com.github.benmanes.caffeine.cache.Cache<Object,Object> localCache
    • locks

      protected final com.github.benmanes.caffeine.cache.Cache<Object,ReentrantLock> locks
    • cacheCircuitBreaker

      protected final io.github.resilience4j.circuitbreaker.CircuitBreaker cacheCircuitBreaker
  • Constructor Details

    • MultiLevelCache

      public MultiLevelCache(String name, MultiLevelCacheConfigurationProperties properties, org.springframework.data.redis.core.RedisTemplate<Object,Object> redisTemplate, com.github.benmanes.caffeine.cache.Cache<Object,Object> localCache, io.github.resilience4j.circuitbreaker.CircuitBreaker cacheCircuitBreaker)
      Initializes a new instance of the MultiLevelCache class with the given parameters.
      Parameters:
      name - The name of the cache.
      properties - The configuration properties for the cache.
      redisTemplate - The Redis template used for accessing the Redis cache.
      localCache - The local cache used as an additional level of caching.
      cacheCircuitBreaker - The circuit breaker used for handling cache failures.
    • MultiLevelCache

      public MultiLevelCache(String name, MultiLevelCacheConfigurationProperties properties, org.springframework.data.redis.cache.RedisCacheWriter redisCacheWriter, org.springframework.data.redis.core.RedisTemplate<Object,Object> redisTemplate, com.github.benmanes.caffeine.cache.Cache<Object,Object> localCache, io.github.resilience4j.circuitbreaker.CircuitBreaker cacheCircuitBreaker)
      Creates a new instance of MultiLevelCache.
      Parameters:
      name - The name of the cache.
      properties - The configuration properties for the cache.
      redisCacheWriter - The Redis cache writer to use.
      redisTemplate - The Redis template used for accessing the Redis cache.
      localCache - The local cache used as an additional level of caching.
      cacheCircuitBreaker - The circuit breaker used for handling cache failures.
  • Method Details

    • lookup

      protected Object lookup(@NonNull Object key)
      Perform an actual lookup in the underlying store.

      We do not allow storing null values, if local cache does not have a mapping for specified key we query Redis using circuit breaker and error handling logic. If Redis contains requested mapping, the value will be saved in the local cache. If Redis is not available, null will be returned.

      Overrides:
      lookup in class org.springframework.data.redis.cache.RedisCache
      Parameters:
      key - the key whose associated value is to be returned
      Returns:
      the raw store value for the key, or null if none
    • get

      @NonNull public <T> T get(@NonNull Object key, @NonNull Callable<T> valueLoader)
      Return the value to which this cache maps the specified key, obtaining that value from valueLoader if necessary. This method provides a simple substitute for the conventional "if cached, return; otherwise create, cache and return" pattern.

      If the valueLoader throws an exception, it is wrapped in a Cache.ValueRetrievalException

      If Redis cannot be queried, valueLoader will still be executed and value will be stored in local cache instead.

      Specified by:
      get in interface org.springframework.cache.Cache
      Overrides:
      get in class org.springframework.data.redis.cache.RedisCache
      Parameters:
      key - the key whose associated value is to be returned
      Returns:
      the value to which this cache maps the specified key
      Throws:
      org.springframework.cache.Cache.ValueRetrievalException - if the valueLoader throws an exception or retrieved value was null
      See Also:
      • AbstractValueAdaptingCache.get(Object)
    • put

      public void put(@NonNull Object key, @Nullable Object value)
      Associate the specified value with the specified key in this cache.

      If the cache previously contained a mapping for this key, the old value replaced by the specified value.

      If value is null specified key will be evicted.

      Actual registration performed in an asynchronous fashion, with later lookups possibly not seeing the entry yet.

      Use putIfAbsent(java.lang.Object, java.lang.Object) for guaranteed immediate registration for current cache.

      Specified by:
      put in interface org.springframework.cache.Cache
      Overrides:
      put in class org.springframework.data.redis.cache.RedisCache
      Parameters:
      key - the key with which the specified value is to be associated
      value - the value to be associated with the specified key
      See Also:
    • putIfAbsent

      public org.springframework.cache.Cache.ValueWrapper putIfAbsent(@NonNull Object key, @Nullable Object value)
      Atomically associate the specified value with the specified key in this cache if it is not set already.

      This is equivalent to:

      
       ValueWrapper existingValue = cache.get(key);
       if (existingValue == null) {
           cache.put(key, value);
       }
       return existingValue;
       
      except that the action performed atomically for current cache.

      If value is null specified key will be evicted.

      Specified by:
      putIfAbsent in interface org.springframework.cache.Cache
      Overrides:
      putIfAbsent in class org.springframework.data.redis.cache.RedisCache
      Parameters:
      key - the key with which the specified value is to be associated
      value - the value to be associated with the specified key
      Returns:
      the value to which this cache maps the specified key (which may be null itself), or also null if the cache did not contain any mapping for that key prior to this call. Returning null is therefore an indicator that the given value has been associated with the key, or it was evicted.
      See Also:
    • evict

      public void evict(@NonNull Object key)
      Evict the mapping for this key from this cache if it is present.

      Actual eviction performed in an asynchronous fashion, with later lookups possibly still seeing the entry. Use evictIfPresent(java.lang.Object) for guaranteed immediate removal for current cache.

      Specified by:
      evict in interface org.springframework.cache.Cache
      Overrides:
      evict in class org.springframework.data.redis.cache.RedisCache
      Parameters:
      key - the key whose mapping is to be removed from the cache
      See Also:
    • evictIfPresent

      public boolean evictIfPresent(@NonNull Object key)
      Evict the mapping for this key from this cache if it is present, expecting the key to be immediately invisible for later lookups.
      Parameters:
      key - the key whose mapping is to be removed from the cache
      Returns:
      true if local cache was known to have a mapping for this key before, false if it did not (or if prior presence could not be determined)
      Since:
      5.2
      See Also:
    • clear

      public void clear()
      Clear the cache through removing all mappings.

      Actual clearing performed in an asynchronous fashion, with later lookups possibly still seeing the entries. Use invalidate() for guaranteed immediate removal of entries for current cache.

      Specified by:
      clear in interface org.springframework.cache.Cache
      Overrides:
      clear in class org.springframework.data.redis.cache.RedisCache
      See Also:
    • invalidate

      public boolean invalidate()
      Invalidate the cache through removing all mappings, expecting all entries to be immediately invisible for later lookups.
      Returns:
      true if local cache was known to have mappings before, false if it did not (or if prior presence of entries could not be determined)
      Since:
      5.2
      See Also: