Implementing Caching Service in Spring Boot using Guava Cache
This article details a simple way to implement Caching Service in Java Spring Boot using Google Guava Cache API.
Need for Cache
Applications are becoming more and more distributed in nature, which requires an increased amount of network traffic in the end-to-end functional flow. The introduction of microservices patterns empowers programmers to easily build-and-maintain applications, but it introduces more network latency in the overall application flow than monolith applications.
To minimize such network latency and increase overall application performance, programmers need to identify opportunities to temporarily store frequently used data locally to avoid redundant/unnecessary network traffic.
What we can cache
- Static data that does not change
- Frequently queried data that does not change for a specific time duration.
- When we use Cached data, we must choose a reasonable expiry duration to invalidate/delete stored entries.
- Whenever possible, store only required data in the Cache to avoid overloading application server memory (especially when using in-memory Cache-Store).
What is Guava Cache
Guava Cache is a library provided by Google to implement a simple in-process Cache Store. Guava Cache is an incremental cache, more like ConcurrentMap, with an additional feature that invalidates entries automatically upon certain configurable expiry conditions.
Implementing Guava Cache Example
Let’s see how we can implement a simple Cache-Store using the Guava Cache library.
The rest of the article details all the steps we need to implement Caching Technique. If it is helpful, you may also download the final source code from GitHub Link — Implementing Guava Cache
Step 1: Create a Spring Boot Project
We need one Spring Boot project. The basic skeleton can be created quickly by using https://start.spring.io/ as below,
Step 2: Write HTTP GET REST API
Write one sample REST API as detailed below. In this API, we are simulating a backend service call by adding an intentional wait time. The next step will introduce the Guava Cache to cache the API response and improve the overall latency.
When we build and run this application, it will produce the below output, which shows some latency as each time call is sent to backend service to fetch data,
Step 3: Add Guava Dependency
In build.gradle file add Guava library to the list of dependencies as,
implementation 'com.google.guava:guava:latest.release'
Step 4: Define a Class to provide Cache-Store for any generic type <T>
Let’s call this class CacheStore, which can help us build a Cache instance for any generic type
Please Note: expireAfterWrite() provides the ability to invalidate the Cache Records accordingly to the supplied parameters.
Step 5: Define a Java Bean to generate a new Cache Store of record type Employee
Step 6: Modify ApiController Class to implement Caching for Employee records
Here in this step, we will store Employee records in Guava Cache.
Please Note:
- For the first request, we will not find the Employee record in Cache. And we need to call backend API to fetch the Employee record. (Here, the backend API has an intentional delay to simulate an HTTP call over the network.)
- The fetched Employee record is must be stored in the Cache first before returning it to the consumer.
- For subsequent requests, we will find the Employee record in the Cache. We can immediately return the cached Employee record in the response by skipping the backend API call.
- In Step #5, we had created Cache-Store with an expiry duration of 120 seconds. Therefore, the LoadingCache will retain the Employee record only for 120 Seconds. Guava Cache will keep track of time and invalidate/remove the Employee record from Cache by itself post 120 seconds.
Step 7: Demo
Build and Run ApiController Application on local. To test Caching go to URL:
http://localhost:8080/employee/1
As this will be the first call, employee key “1” will not be found in Cache. Therefore, a backend service call will be triggered, and we will see some latency in response.
Test the same URL a second time, and you will see improved response time as the record from Cache is used by skipping the backend service call.
Next, test for another employee id as:
http://localhost:8080/employee/2
This new employee key “2” is not present in Cache. Therefore, a backend service call is triggered to fetch the details.
Refer to sequential logs below for all the 3 tests:
Step 8: Reusing CacheStore<T> class to create another Cache-Store of a different record type.
Once we follow Steps #1 thru #6, we will have provision to quickly add a new Cache-Store of any number of record types.
Refer to the below example to know what all changes needed to generate any new Cache-Store:
Please Note: Here, Product.java and ProductService.Java follow the same pattern as previously provided Employee.Java and EmployeeService.Java. If you are interested, you may download the complete source code from GitHub Link — Implementing Guava Cache.
Please note that productNameCache store is caching only the required field even if the backend service is returning a larger object. That avoids unnecessary overloading of application server memory.
Summary
In this article, we have seen a simple way to implement Guava Cache in Spring Boot.
Download the complete source code for this article from GitHub Link — Implementing Guava Cache
Happy Coding!