Different Caching Strategies in Java Application

two brown and black leather handbags

Request Scoped Caching

Request Scoped Caching allows us to cache the object for the lifetime of the request, to implement this we will be using Caffeine Cache, as it is one of the high-performance Cache in Java.

  • Features of Caffeine Cache
    • Caffeine is a Java 8 rewrite of Guava’s cache that supersedes support for Guava. 
    • Good fit for Short TTL Keys and provides very fast retrievals
    • Easy implementation as no additional resources are needed.

Implementation Steps

Step1: Add Dependencies

        <dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
            <version>${caffeine.version}</version>
        </dependency>

Step2: Configuration

 @Bean
  @RequestScope
  public CacheManager cacheManager() {
    CaffeineCacheManager cacheManager = new CaffeineCacheManager("OBJECT");
    cacheManager.setCaffeine(Caffeine.newBuilder());
    return cacheManager;
  }

Step3: Cache Objects

  @Cacheable(
      value = "OBJECT",
      key = "'obj:'+ #id + ':' + @instance.getKey()",
      condition = "@instance.isCachingAllowed()",
      cacheManager = "cacheManager")
  public Object find(@NotBlank String id) {
    ----
    ----
  }

The Object is returned from Request Cache based on the below conditions.
key: The key used to retrieve the value from the cache
condition: The key is returned only if the Condition is satisfied.
cacheManager: The instance for the cache Manager
value: The name of CaffeineCacheManager

Session Scoped Caching

Session Cache is another cache that is available till the session is present, hence its lifetime is larger than request, Sessions are cleared usually after 4-5 hours, We have again used caffeine to implement this cache. Given below the configuration, The rest of it is the same as the above implementation.

Step1: Configuration

   @Bean
  @SessionScope
  public CacheManager cacheManager() {
    CaffeineCacheManager cacheManager = new CaffeineCacheManager("OBJECT");
    cacheManager.setCaffeine(Caffeine.newBuilder());
    return cacheManager;
  }

Instance Scoped Caching

Instance Scoped Caching is available throughout the Instance lifetime, the values are evicted either when the instance is restarted or when the conditions of eviction are met. We will be using a Guave cache here that provides a robust size-based and Least Recent Used based eviction. The below method of loading the cache takes a Function, which is used for initializing values, similar to the get method of the manual strategy.

Step1: Create LoadingCache

         <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>${guava.version}</version>
        </dependency>

Step2: Create LoadingCache

     LoadingCache<String, DataObject> cache = CacheBuilder.newBuilder()
            .maximumSize(cacheSize)
            .expireAfterAccess(expiryTime, TimeUnit.MINUTES)
            .removalListener((RemovalListener<String, DataObject>)
                    removal -> {
                      DataObject ds = removal.getValue();
                      // Do necessary operations for removal
                    })
            .build(k -> DataObject.get("Data for " + k));
  • Conditions In EvictingCache
  • Size > cacheSize, then based > on LRU the item is evicted, and on eviction, we will call the close method
  • Time spend after last access > expiry time
  • Removal Listener
  • When the Item is evicted, then the item is passed to the removal listener, where we can complete housekeeping operations like close connection, logging, etc

Distributed Scoped Caching – Redis

  • Redis is an open-source, in-memory data structure store that is used by millions of developers and can be used as a Cache.
  • Redis provides data structures such as strings, and stores as Store JSON objects.
  • Redis provides high availability, eviction, transaction, and scalability.
  • The size of the Redis cache is limited to the size of the RAM.
  • Redis can handle at least 250 million keys per instance and can store values of sizes up to 512MB.

Step1: Add Dependencies

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
    <version>2.4.3</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.4.3</version>
</dependency>

Step2: Configuration

 @Bean
public RedisCacheConfiguration cacheConfiguration() {
    return RedisCacheConfiguration.defaultCacheConfig()
      .entryTtl(Duration.ofMinutes(60))
      .disableCachingNullValues()
      .serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
}

Step3: Get Values from Redis Caching

@Cacheable(value = "objectCache")
public Object getObject(String id) {
    return objectRepository.findById(id)
      .orElseThrow(RuntimeException::new);
}

In the above code, the first invocation would get the object from the repository or downstream service and the second from the cache, the main advantage of Redis is that it is outside of your instance, hence can work independently of your server resources. This feature helps us to maintain the state between different instances, have easy scalability, and better seperation of concerns.

error: Content is protected !!
Scroll to Top