Issue
I have a Spring Boot (2.3.1.RELEASE) app that has caching with Redis Sentinel. This is my configuration for the Sentinel connection:
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
.master(redisProperties.getSentinel().getMaster());
redisProperties.getSentinel().getNodes().forEach(s -> sentinelConfig.sentinel(s, redisProperties.getPort()));
sentinelConfig.setPassword(RedisPassword.of(redisProperties.getPassword()));
return new LettuceConnectionFactory(sentinelConfig);
}
And this is my caching manager configuration:
@Bean
public RedisCacheManager cacheManager() {
Map<String, RedisCacheConfiguration> cacheConfigs = new HashMap<>();
cacheConfigs.put("cache1", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(ttlMinutes)));
cacheConfigs.put("cache2", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(ttlMinutes)));
return RedisCacheManager.builder(redisConnectionFactory())
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(ttlMinutes)))
.withInitialCacheConfigurations(cacheConfigs)
.transactionAware()
.build();
}
Everything works fine from caching perspective.
However, If I turn on debug logs inside the io.lettuce.core.protocol.CommandHandler
, I see that it is always connecting to the same node (master). Which I can confirm by looking at the logs on the node.
Everywhere I look online, this seems like the correct configuration.
This brings me to my question:
- Is it possible to configure the Spring caching abstraction to use the master node only for writes and the slave nodes for reads?
Is this expectation even valid? Or this is the way Sentinel is supposed to be used (all requests go to master)?
Solution
Yes, it can be done.
From Spring Data Redis Docs - 10.4.4. Write to Master, Read from Replica :
It is said that Spring Data Redis provides a Redis Master/Replica setup which not only allows data to be safely stored at more nodes but also allows reading data from replicas while pushing writes to the master by using Lettuce.
For this, you have to update redisConnectionFactory()
method in the configuration class :
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.readFrom(ReadFrom.REPLICA_PREFERRED)
.build();
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
.master(redisProperties.getSentinel().getMaster());
redisProperties.getSentinel().getNodes().forEach(s -> sentinelConfig.sentinel(s, redisProperties.getPort()));
sentinelConfig.setPassword(RedisPassword.of(redisProperties.getPassword()));
return new LettuceConnectionFactory(sentinelConfig, clientConfig);
}
Answered By - Anish B.
Answer Checked By - Cary Denson (JavaFixing Admin)