Caching with ElastiCache(Redis) and Spring Boot

ElastiCache is an AWS managed in memory database. It can be used for many cases. Here I will integrate ElastiCache in my spring boot project for caching. The code can be found here: https://github.com/olein/Java-AWS-RnD/tree/elasti-cache

Let's learn a few things about ElastiCache before I integrate this in my project.

  1. Performance: ElastiCache is used for caching that may significantly improve application performance if applied properly.

  2. Fully managed: It is fully managed by AWS means we will not have to upgrade or maintain anything about it. AWS will upgrade to new versions and manage the deployed clusters.

  3. Scalable: It is easily scalable. We can create a cluster with few clicks.

Let's create a cluster in AWS

Go to ElastiCache dashboard.

DashboardDashboard

From the left side click on Redis and you can see a list of already created clusters. Now I do not have any clusters.

image

Click on create.

image

We need to select Redis and enable cluster mode. The location should be Amazon cloud.

SettingsSettings

Now the settings of Redis. Give a proper name and description of the cluster.

For our case I select a t2.micro. I disabled Multi-AZ support. I already had a subnet. you can create your own subnet to manage the redis cluster.

Maximum number of shards can be 90 and replicas per shard can be 15. If you need more then you will need to contact AWS support to update the limit for your account.

image

For advanced settings you can select the specific region to shard and replica distribution. You can leave the other settings as default. Now click on create. The cluster will be created in a few minutes.

Creating clusterCreating cluster

image

After successful creation the status will be available.

Detail for the clusterDetail for the cluster

Here you can see the configuration endpoint. Although to integrate we will need the endpoint for each shard.

Few things to remember

  1. ElastiCache cluster can not be accessed from outside the VPC.

  2. We will have to deploy the application in an EC2 and allow outbound port 6379 for the EC2 instance.

  3. Also the security group bound to Redis cluster need to allow inbound rule for port 6379.

Application setup

Now let's setup our project to use this cluster. First we need to add maven dependencies in our pom.xml file.

<dependency>
  <groupId>org.springframework.data</groupId>
  <artifactId>spring-data-redis</artifactId>
</dependency>
<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
</dependency>

Redis configuration:

@Configuration
@EnableCaching
public class RedisConfiguration { *

  *List<String> clusterNodes =
      Arrays.*asList*(
          "node1:6379",
          "node2:6379",
          "node3:6379");

  @Bean
  RedisConnectionFactory connectionFactory() {
    return new JedisConnectionFactory(new RedisClusterConfiguration(clusterNodes));
  }

  @Bean(value = "redisTemplate")
  public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
    template.setKeySerializer(new StringRedisSerializer());
    template.setHashKeySerializer(new StringRedisSerializer());
    template.setConnectionFactory(factory);
    return template;
  }

  @Primary
  @Bean(name = "cacheManager1Hr") *
  *public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
    Duration expiration = Duration.*ofHours*(1);
    RedisCacheManager redisCacheManager =
        RedisCacheManager.*builder*(redisConnectionFactory)
            .cacheDefaults(
                RedisCacheConfiguration.*defaultCacheConfig*()
                    .disableCachingNullValues()
                    .serializeValuesWith(
                        RedisSerializationContext.SerializationPair.*fromSerializer*(
                            new GenericJackson2JsonRedisSerializer()))
                    .entryTtl(expiration))
            .build();

    redisCacheManager.setTransactionAware(false);
    return redisCacheManager;
  }
}

Here, in the configuration class I enabled the caching and setup caching time to 1hr maximum. Endpoints of cluster nodes are also provided. Spring boot will use these configurations and connect to the cluster.

@Cacheable(value = "CACHE", key = "{#name}", cacheManager = "cacheManager1Hr")
public String cacheMethod(String name) {
  *LOG*.info("Method cached " + name);
  return "This method will cache the result " + name;
}

@Caching(evict = {@CacheEvict(cacheNames = "CACHE", key = "{#name}")})
public String clearCache(String name) {
  *LOG*.info("Cache cleared " + name);
  return "This method will clear cache " + name;
}

I will not cover anything about caching implementation in spring boot but this is a sample code just to test if the integration works :)

How to manage the application if the Redis server is down

This is a very critical situation. If the Redis server is down or somehow application can not access Redis server then all the cached api will start throwing errors. Even if the application and database are fully functional. In a production server that is a nightmare😨. I have added configuration to manage this situation 😃

@Configuration
public class CachingConfiguration extends CachingConfigurerSupport {
  @Override
  public CacheErrorHandler errorHandler() {
    return new CustomCacheErrorHandler();
  }
}

public class CustomCacheErrorHandler implements CacheErrorHandler {

  private static final Logger *LOG *= LoggerFactory.*getLogger*(CustomCacheErrorHandler.class);

  @Override
  public void handleCacheGetError(RuntimeException e, Cache cache, Object o) {
    *LOG*.error("Error while getting cache from redis");
  }

  @Override
  public void handleCachePutError(RuntimeException e, Cache cache, Object o, Object o1) {
    *LOG*.error("Error while putting cache from redis");
  }

  @Override
  public void handleCacheEvictError(RuntimeException e, Cache cache, Object o) {
    *LOG*.error("Error while evict cache from redis");
  }

  @Override
  public void handleCacheClearError(RuntimeException e, Cache cache) {
    *LOG*.error("Error while clearing cache from redis");
  }
}

In case Redis throws an error, our custom error handler will catch the error. As a result the application will treat this as a cache miss and fetch data from the actual data source. The response time will be a bit slow but the application will work properly. We can analyze the error log later to understand when the server became unavailable.

That is all. Hope this will help you to start using ElastiCache. Best of luck! 😊

Enjoyed this article?

Share it with your network to help others discover it

Continue Learning

Discover more articles on similar topics