CacheLayer - fincity-india/nocode-saas GitHub Wiki

Introduction

We have implemented caching in the platform to provide the ability to cache the data and the resources used in the application. It helps in reducing the load on the server and trip to database, provides a faster response to the user. We are relying on multi layer caching, the first layer is the in-memory cache called Caffeine and the second layer is the Redis cache.

To disable the cache we can set the spring.cache.type to none in the configuration file.

We are not using the default cache manager provided by Spring and any of the annotations to cache the data here. One of the primary reasons is to manage the cache based on the client, application and other factors like user. The other reason is to integrate the Redis into this cache layer. To achieve this we use the CacheService. The methods in this service will provide the ability to cache in the way we need.

sequenceDiagram
    AnyService->>+CacheService: Get data from [cache] with [key]
    alt is available
        CacheService->>+Caffeine: Get data from [cache] with [key]
        Caffeine-->>-CacheService: Data    
    else not available
        CacheService->>+Caffeine: Get data from [cache] with [key]
        Caffeine-->>-CacheService: null
        CacheService->>+Redis: Get data from [hash] with [key]
        Redis-->>-CacheService: Data
    end
    CacheService-->>-AnyService: Data

The following diagram will show the sequence of events when evicting from cache.

sequenceDiagram
    AnyService->>+CacheService: Evict from [cache] with [key]
    CacheService->>+Redis: Issue Async Command to evict from [cache] with [key]
    Redis-->-CacheService:Done
    CacheService->>+Redis: Evict from [cache] with [key]
    Redis-->>-CacheService:Done
    CacheService-->>-AnyService: Done
    Redis-)CacheService: Message command to evict from [cache] with [key]
    CacheService->>+Caffeine: Evict from [cache] with [key]
    Caffeine-->>-CacheService: Done

Strategy

Please follow the "read through cache" strategy in your services. This means that you should first read from the cache and if the data is not available in the cache, then read from the database and then write to the cache. This will ensure that the cache is always up-to-date. When writing into the DB, in either create, update or delete methods, the cache should be evicted based on your keys.

In some cases, you may need to write the empty values to cache. This is to avoid the database reads when the data is not available in database. This is a rare case and should be used with caution.

In very rare cases, you may need to write to the cache first and then to the database. This is only when you are sure that the data will be written to the database. In such cases, you should handle the cache and database write in a transactional manner. Please make sure that you are aware of the implications of this strategy.

How to use Cache Service

The CacheService can be auto wired into any spring service or component.

When the service starts we shall start listening to any commands from Redis to evict the cache. This is to ensure that the cache is in sync with the database. The cache will be evicted based on the key and name of the cache.

All the methods in this service honor if the cache type is NONE and don't cache and return null all the time when getting the data from the cache.

All methods that take a key to retrieve or store data will construct the key based on the variable length arguments given to the method. The key will be constructed by joining the arguments. The key will be constructed in the following way.

// Name of the cache is "PersonsCache"
// Key is "one"
// Value that is stored in the cache is "person1"
cacheService.put("PersonsCache", "person1", "o", "n", "e");

cacheService.get("PersonsCache", "o", "n", "e"); // returns Mono<String> with value "person1"

The following methods are available for use.

  • put method stores the value in the cache with the key name in the cache name. The value will be stored in both Caffeine and Redis.

  • cacheValueOrGet method retrieves the value from the cache with the key name in the cache name. If the value is not available in Caffeine, then it will be retrieved from Redis and stored in Caffeine. If the value is not available in Redis, then the value will be retrieved from the source by calling the supplier function given to this method and stored in both Caffeine and Redis.

  • cacheEmptyValueOrGet method retrieves the value from the cache with the key name in the cache name. If the value is not available in Caffeine, then it will be retrieved from Redis and stored in Caffeine. If the value is not available in Redis, then the value will be retrieved from the source by calling the supplier function given to this method and stored in both Caffeine and Redis. Even when source returns null, the null value will be stored in both Caffeine and Redis.

    • We use CacheObject class to store any value in this case to differentiate between null and empty value.
  • get method retrieves the value from the cache with the key name in the cache name. If the value is not available in Caffeine, then it will be retrieved from Redis and stored in Caffeine. If the value stored is of type CacheObject then the value stored in the CacheObject object is returned even if it is null.

  • evict method evicts the value from the cache with the key name in the cache. The value will be evicted from both Caffeine and Redis.

  • evictAll method evicts all the values from the cache with the cache name. The values will be evicted from both Caffeine and Redis.

  • evictAllCaches method evicts all the values from all the caches. The values will be evicted from both Caffeine and Redis.

⚠️ **GitHub.com Fallback** ⚠️