Home/Synchronous And Asynchronous Caching

Synchronous And Asynchronous Caching

Published On: 26 December 2022.By .
  • General

As we discussed what is Caching and merits and demerits of it in my previous blog Caching-I. In this, we will learn about how to do Synchronous caching using annotations and Asynchronous caching using Caffeine.

Synchronous Caching :

We are doing the synchronous caching using the annotations with simple spring boot project.

  • Dependency to be used :
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-cache</artifactId>
      <version>2.7.2</version>
</dependency>
  • To enable the caching use @EnableCaching annotation to any of the configuration class
@SpringBootApplication
@EnableCaching
public class ReactDemoApplication {
        public static void main(String[] args) {
               SpringApplication.run(ReactDemoApplication.class, args);
        }
}
  • We can customize the cache manager bean like we can set the time after which the value will expire from the cache and we can also set the maximum size of the cache and many more things.
  • Here we will also set the name of the cache that will pass in the @Cacheable annotation.
@Configuration
public class AppConfig extends CachingConfigurerSupport {

    @Bean
    @Override
    public CacheManager cacheManager() {
         ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager() {

            @Override
            protected Cache createConcurrentMapCache(final String name) {
                return new ConcurrentMapCache(name, CacheBuilder.newBuilder()
                        .expireAfterWrite(10, TimeUnit.SECONDS)
                        .maximumSize(100).build().asMap(), false);
            }
        };

        cacheManager.setCacheNames(Arrays.asList("Activity-cache"));
        return cacheManager;
    }
}
  • Next step is to bind the behaviour of caching with its methods:

1. @Cacheable : annotation used for adding the entity in the cache.

2. @CachePut : annotation used for updating or adding the particular entity in the cache.

3. @CacheEvict : used to flush the cache.

@RequestMapping(value = "/deck/countSynSave", method = RequestMethod.GET)
@Cacheable(value = "Activity-cache", key = "#root.methodName")
public Object randomActivitySyn(ServerHttpRequest request) {
     return webService.activityResponseBlocked();
}

When we hit this request, firstly it will call the service and stores our value in the cache name “Activity-cache” and when we call the request again then it fetches the value from the cache if it does not expire from the cache.

In this way we do the synchronous caching.

Asynchronous Caching :

We can also do the Asynchronous Caching by using annotation :

@RequestMapping(value = "/deck/countAsyncByAnnotation", method = RequestMethod.GET)
@Cacheable(value = "Activity-cache")
public CompletableFuture randomActivityAsyncByAnnotation(ServerHttpRequest request) {
   return webService.activity().toFuture();
}

Why we prefer Caffeine Cache for Asynchronous Caching:

Caffeine is a high-performance Java caching library providing a near optimal hit rate.

A Caffeine is similar to ConcurrentMap, but not quite the same. The most fundamental difference is that a ConcurrentMap persists all elements that are added to it until they are explicitly removed. A Caffeine on the other hand is generally configured to evict entries automatically, in order to constrain its memory footprint. In some cases AsyncLoadingCache can be useful even if it doesn’t evict entries, due to its automatic cache loading.

  • Dependecy to be used :
<dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
        <version>3.1.1</version>
</dependency>
  • We have to make the bean of the Asynchronous cache and we can set the size and the expiry time of the cache values
@Configuration
public class Config  {
    @Bean
    AsyncCache<Object, Object> getCache(){
        return Caffeine.newBuilder()
                .maximumSize(10)
                .expireAfterWrite(30, TimeUnit.SECONDS)
                .buildAsync();
    }
}
  • Now, we will autowire the object of the Async cache.
  • And we will store the value in the cache by using the .put method which accepts the 2 parameters- first is the I’d and the second is value to be cached.
  • To fetch the value from cache we have to pass the I’d in the .getIfPresent method.
@RestController
@RequestMapping("/api")
public class WebController {

    @Autowired private WebService webService;
    @Autowired private AsyncCache<Object, Object> getCache;

    @RequestMapping(value = "/deck/countAsyn", method = RequestMethod.GET)
    public Object randomActivityAsyn(ServerHttpRequest request) {
        Object obj = getCache.getIfPresent("Random Activity");
        if(obj != null) return obj;

        Mono<ResponseWrapper> responseWrapperMono = webService.activity()
                .map(result -> ResponseWrapper.successResponse("Random Activity", result, request.getPath().toString()))
                .defaultIfEmpty(ResponseWrapper.errorResponse("Internal server error", null, request.getPath().toString()));

        getCache.put("Random Activity", responseWrapperMono.toFuture());

        return responseWrapperMono;
    }
}

In this way we do the Asynchronous caching using caffeine.

Related content

We Love Conversations

Say Hello
Go to Top