Spring Boot Redis Cache using R2DBC

Prateek
3 min readDec 12, 2022

--

In this example, we’ll learn how to make the use of Redis cache when you’ve a requirement where 90% operations are HTTP GET (Read) and concurrency is high (may be 300 tps or more), then using Spring WebFlux Redis with R2DBC would be good choice.

Say, user(s) is/are making (concurrent) request(s) to product-service to get the details of whole product based on ProductId. You know the fact that product details will not changes much (often/rarely), in such cases — always going to database (here Postgres DB) to fetch the product details would not be a ideal case with the micro-services architecture, it would lead to tremendous network calls and not affordable when you’re in public cloud like Azure, AWS. Instead infact you can bring some caching mechanism to return data back directly from cache when product data available. Off-course you need to smartly maintain duration of cache (Time to live) and it need to be updated real-time.

Case: Assume clients making HTTP GET requests to get the product details based on Product Id, we can perform lookup within the Redis cache, if product data is available, it need to be return back directly from cache. If Product details doesn’t exists within the cache, then we can always bring data from persistent store, update the cache and return back to the client. This will avoid numerous calls to the database and make the system more robust, performant and cheap from cost perspective in cloud. The same can be done when you’re adding, updating and remiving the elements.

As you can see below in screen shot, I’m making a call for Product Id 99, initially it perform lookup in redis cache, it doesn’t found, the call goes to PostgresDB to get the Product details, before returning back to the client, it has updated the redis cache. When again some other client makes a request for the same product Id, data is return back from the redis cache itself. Since we’re leveraging the reactive programming it would be very fast and optimal solution.

Below is the code snippet which will do the magic

@Service
@ConditionalOnProperty(name = "cache.enabled", havingValue = "true")
public class ProductServiceWithRedisCache extends ProductServiceWithNoCache {

private static final String KEY = "product";

@Autowired
private ReactiveHashOperations<String, Integer, ProductDto> hashOperations;

@Override
public Mono<ProductDto> getProduct(Integer id) {
return hashOperations.get(KEY, id)
.switchIfEmpty(this.getFromDatabaseAndCache(id));
}

@Override
public Mono<Void> updateProduct(Integer id, Mono<ProductDto> mono) {
return super.updateProduct(id, mono)
.then(this.hashOperations.remove(KEY, id))
.then();
}

private Mono<ProductDto> getFromDatabaseAndCache(Integer id) {
return super.getProduct(id)
.flatMap(dto -> this.hashOperations.put(KEY, id, dto)
.thenReturn(dto));
}
}

You can maintain cache enabled and disabled logic from properties file too

spring.r2dbc.url=r2dbc:postgresql://localhost:5432/postgres
spring.r2dbc.username=postgres
spring.r2dbc.password=postgres
cache.enabled=true

I will also show the with and without cache enabled results from Jmeter too. I am currently working on that — will update soon.

--

--

Prateek
Prateek

Written by Prateek

Java Developer and enthusiast

No responses yet