Synchronous And Asynchronous Caching
- General
Synchronous And Asynchronous Caching
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 :
1 2 3 4 5 |
<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
1 2 3 4 5 6 7 |
@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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@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.
1 2 3 4 5 |
@RequestMapping(value = "/deck/countSynSave", method = RequestMethod.GET) @Cacheable(value = "Activity-cache", key = "#root.methodName") public Object randomActivitySyn(ServerHttpRequest request) { return webService.activityResponseBlocked(); } |
In this way we do the synchronous caching.
Asynchronous Caching :
We can also do the Asynchronous Caching by using annotation :
1 2 3 4 5 |
@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 :
1 2 3 4 5 |
<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
1 2 3 4 5 6 7 8 9 10 |
@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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@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; } } |
Related content
Auriga: Leveling Up for Enterprise Growth!
Auriga’s journey began in 2010 crafting products for India’s