Skip to content

Commit 911578e

Browse files
committed
Merge pull request #46957 from facewise
* pr/46957: Polish "Add support for static master-replica with Lettuce" Add support for static master-replica with Lettuce Closes gh-46957
2 parents 319e462 + fbcc1fd commit 911578e

File tree

10 files changed

+235
-13
lines changed

10 files changed

+235
-13
lines changed

documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/data/nosql.adoc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,10 @@ spring:
7575
TIP: You can also register an arbitrary number of beans that implement javadoc:org.springframework.boot.data.redis.autoconfigure.LettuceClientConfigurationBuilderCustomizer[] for more advanced customizations.
7676
javadoc:io.lettuce.core.resource.ClientResources[] can also be customized using javadoc:org.springframework.boot.data.redis.autoconfigure.ClientResourcesBuilderCustomizer[].
7777
If you use Jedis, javadoc:org.springframework.boot.data.redis.autoconfigure.JedisClientConfigurationBuilderCustomizer[] is also available.
78-
Alternatively, you can register a bean of type javadoc:org.springframework.data.redis.connection.RedisStandaloneConfiguration[], javadoc:org.springframework.data.redis.connection.RedisSentinelConfiguration[], or javadoc:org.springframework.data.redis.connection.RedisClusterConfiguration[] to take full control over the configuration.
78+
79+
Alternatively, you can register a bean of type javadoc:org.springframework.data.redis.connection.RedisStandaloneConfiguration[], javadoc:org.springframework.data.redis.connection.RedisSentinelConfiguration[], javadoc:org.springframework.data.redis.connection.RedisClusterConfiguration[], or javadoc:org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration[]] to take full control over the configuration.
80+
81+
NOTE: master/replica is not supported by Jedis.
7982

8083
If you add your own javadoc:org.springframework.context.annotation.Bean[format=annotation] of any of the auto-configured types, it replaces the default (except in the case of javadoc:org.springframework.data.redis.core.RedisTemplate[], when the exclusion is based on the bean name, `redisTemplate`, not its type).
8184

module/spring-boot-data-redis/src/main/java/org/springframework/boot/data/redis/autoconfigure/DataRedisConnectionConfiguration.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.springframework.data.redis.connection.RedisPassword;
3434
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
3535
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
36+
import org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration;
3637
import org.springframework.util.Assert;
3738
import org.springframework.util.ClassUtils;
3839

@@ -62,6 +63,8 @@ abstract class DataRedisConnectionConfiguration {
6263

6364
private final @Nullable RedisClusterConfiguration clusterConfiguration;
6465

66+
private final @Nullable RedisStaticMasterReplicaConfiguration masterReplicaConfiguration;
67+
6568
private final DataRedisConnectionDetails connectionDetails;
6669

6770
protected final Mode mode;
@@ -70,11 +73,13 @@ protected DataRedisConnectionConfiguration(DataRedisProperties properties,
7073
DataRedisConnectionDetails connectionDetails,
7174
ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
7275
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
73-
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
76+
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider,
77+
ObjectProvider<RedisStaticMasterReplicaConfiguration> masterReplicaConfiguration) {
7478
this.properties = properties;
7579
this.standaloneConfiguration = standaloneConfigurationProvider.getIfAvailable();
7680
this.sentinelConfiguration = sentinelConfigurationProvider.getIfAvailable();
7781
this.clusterConfiguration = clusterConfigurationProvider.getIfAvailable();
82+
this.masterReplicaConfiguration = masterReplicaConfiguration.getIfAvailable();
7883
this.connectionDetails = connectionDetails;
7984
this.mode = determineMode();
8085
}
@@ -143,6 +148,25 @@ protected final RedisStandaloneConfiguration getStandaloneConfig() {
143148
return null;
144149
}
145150

151+
protected final @Nullable RedisStaticMasterReplicaConfiguration getMasterReplicaConfiguration() {
152+
if (this.masterReplicaConfiguration != null) {
153+
return this.masterReplicaConfiguration;
154+
}
155+
if (this.connectionDetails.getMasterReplica() != null) {
156+
List<Node> nodes = this.connectionDetails.getMasterReplica().getNodes();
157+
RedisStaticMasterReplicaConfiguration config = new RedisStaticMasterReplicaConfiguration(
158+
nodes.get(0).host(), nodes.get(0).port());
159+
nodes.stream().skip(1).forEach((node) -> config.addNode(node.host(), node.port()));
160+
config.setUsername(this.connectionDetails.getUsername());
161+
String password = this.connectionDetails.getPassword();
162+
if (password != null) {
163+
config.setPassword(RedisPassword.of(password));
164+
}
165+
return config;
166+
}
167+
return null;
168+
}
169+
146170
private List<RedisNode> getNodes(Cluster cluster) {
147171
return cluster.getNodes().stream().map(this::asRedisNode).toList();
148172
}
@@ -191,12 +215,15 @@ private Mode determineMode() {
191215
if (getClusterConfiguration() != null) {
192216
return Mode.CLUSTER;
193217
}
218+
if (getMasterReplicaConfiguration() != null) {
219+
return Mode.MASTER_REPLICA;
220+
}
194221
return Mode.STANDALONE;
195222
}
196223

197224
enum Mode {
198225

199-
STANDALONE, CLUSTER, SENTINEL
226+
STANDALONE, CLUSTER, MASTER_REPLICA, SENTINEL
200227

201228
}
202229

module/spring-boot-data-redis/src/main/java/org/springframework/boot/data/redis/autoconfigure/DataRedisConnectionDetails.java

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,32 +58,41 @@ public interface DataRedisConnectionDetails extends ConnectionDetails {
5858
}
5959

6060
/**
61-
* Redis standalone configuration. Mutually exclusive with {@link #getSentinel()} and
62-
* {@link #getCluster()}.
61+
* Redis standalone configuration. Mutually exclusive with {@link #getSentinel()},
62+
* {@link #getCluster()} and {@link #getMasterReplica()}.
6363
* @return the Redis standalone configuration
6464
*/
6565
default @Nullable Standalone getStandalone() {
6666
return null;
6767
}
6868

6969
/**
70-
* Redis sentinel configuration. Mutually exclusive with {@link #getStandalone()} and
71-
* {@link #getCluster()}.
70+
* Redis sentinel configuration. Mutually exclusive with {@link #getStandalone()},
71+
* {@link #getCluster()} and {@link #getMasterReplica()}.
7272
* @return the Redis sentinel configuration
7373
*/
7474
default @Nullable Sentinel getSentinel() {
7575
return null;
7676
}
7777

7878
/**
79-
* Redis cluster configuration. Mutually exclusive with {@link #getStandalone()} and
80-
* {@link #getSentinel()}.
79+
* Redis cluster configuration. Mutually exclusive with {@link #getStandalone()},
80+
* {@link #getSentinel()} and {@link #getMasterReplica()}.
8181
* @return the Redis cluster configuration
8282
*/
8383
default @Nullable Cluster getCluster() {
8484
return null;
8585
}
8686

87+
/**
88+
* Redis master replica configuration. Mutually exclusive with
89+
* {@link #getStandalone()}, {@link #getSentinel()} and {@link #getCluster()}.
90+
* @return the Redis master replica configuration
91+
*/
92+
default @Nullable MasterReplica getMasterReplica() {
93+
return null;
94+
}
95+
8796
/**
8897
* Redis standalone configuration.
8998
*/
@@ -201,6 +210,20 @@ interface Cluster {
201210

202211
}
203212

213+
/**
214+
* Redis master replica configuration.
215+
*/
216+
interface MasterReplica {
217+
218+
/**
219+
* Static nodes to use. This represents the full list of cluster nodes and is
220+
* required to have at least one entry.
221+
* @return the nodes to use
222+
*/
223+
List<Node> getNodes();
224+
225+
}
226+
204227
/**
205228
* A node in a sentinel or cluster configuration.
206229
*

module/spring-boot-data-redis/src/main/java/org/springframework/boot/data/redis/autoconfigure/DataRedisProperties.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ public class DataRedisProperties {
9494

9595
private @Nullable Cluster cluster;
9696

97+
private @Nullable Masterreplica masterreplica;
98+
9799
private final Ssl ssl = new Ssl();
98100

99101
private final Jedis jedis = new Jedis();
@@ -200,6 +202,14 @@ public void setCluster(@Nullable Cluster cluster) {
200202
this.cluster = cluster;
201203
}
202204

205+
public @Nullable Masterreplica getMasterreplica() {
206+
return this.masterreplica;
207+
}
208+
209+
public void setMasterreplica(@Nullable Masterreplica masterreplica) {
210+
this.masterreplica = masterreplica;
211+
}
212+
203213
public Jedis getJedis() {
204214
return this.jedis;
205215
}
@@ -354,6 +364,26 @@ public void setMaxRedirects(@Nullable Integer maxRedirects) {
354364

355365
}
356366

367+
/**
368+
* Master Replica properties.
369+
*/
370+
public static class Masterreplica {
371+
372+
/**
373+
* Static list of "host:port" pairs to use, at least one entry is required.
374+
*/
375+
private @Nullable List<String> nodes;
376+
377+
public @Nullable List<String> getNodes() {
378+
return this.nodes;
379+
}
380+
381+
public void setNodes(@Nullable List<String> nodes) {
382+
this.nodes = nodes;
383+
}
384+
385+
}
386+
357387
/**
358388
* Redis sentinel properties.
359389
*/

module/spring-boot-data-redis/src/main/java/org/springframework/boot/data/redis/autoconfigure/JedisConnectionConfiguration.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.springframework.data.redis.connection.RedisConnectionFactory;
3939
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
4040
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
41+
import org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration;
4142
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
4243
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder;
4344
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisSslClientConfigurationBuilder;
@@ -66,9 +67,10 @@ class JedisConnectionConfiguration extends DataRedisConnectionConfiguration {
6667
ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
6768
ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration,
6869
ObjectProvider<RedisClusterConfiguration> clusterConfiguration,
70+
ObjectProvider<RedisStaticMasterReplicaConfiguration> masterReplicaConfiguration,
6971
DataRedisConnectionDetails connectionDetails) {
7072
super(properties, connectionDetails, standaloneConfigurationProvider, sentinelConfiguration,
71-
clusterConfiguration);
73+
clusterConfiguration, masterReplicaConfiguration);
7274
}
7375

7476
@Bean
@@ -104,6 +106,7 @@ private JedisConnectionFactory createJedisConnectionFactory(
104106
Assert.state(sentinelConfig != null, "'sentinelConfig' must not be null");
105107
yield new JedisConnectionFactory(sentinelConfig, clientConfiguration);
106108
}
109+
case MASTER_REPLICA -> throw new IllegalStateException("'masterReplicaConfig' is not supported by Jedis");
107110
};
108111
}
109112

module/spring-boot-data-redis/src/main/java/org/springframework/boot/data/redis/autoconfigure/LettuceConnectionConfiguration.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.springframework.data.redis.connection.RedisConnectionFactory;
5050
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
5151
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
52+
import org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration;
5253
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
5354
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration.LettuceClientConfigurationBuilder;
5455
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
@@ -74,9 +75,10 @@ class LettuceConnectionConfiguration extends DataRedisConnectionConfiguration {
7475
ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
7576
ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
7677
ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider,
78+
ObjectProvider<RedisStaticMasterReplicaConfiguration> masterReplicaConfiguration,
7779
DataRedisConnectionDetails connectionDetails) {
7880
super(properties, connectionDetails, standaloneConfigurationProvider, sentinelConfigurationProvider,
79-
clusterConfigurationProvider);
81+
clusterConfigurationProvider, masterReplicaConfiguration);
8082
}
8183

8284
@Bean(destroyMethod = "shutdown")
@@ -132,6 +134,11 @@ private LettuceConnectionFactory createConnectionFactory(
132134
Assert.state(sentinelConfig != null, "'sentinelConfig' must not be null");
133135
yield new LettuceConnectionFactory(sentinelConfig, clientConfiguration);
134136
}
137+
case MASTER_REPLICA -> {
138+
RedisStaticMasterReplicaConfiguration masterReplicaConfiguration = getMasterReplicaConfiguration();
139+
Assert.state(masterReplicaConfiguration != null, "'masterReplicaConfig' must not be null");
140+
yield new LettuceConnectionFactory(masterReplicaConfiguration, clientConfiguration);
141+
}
135142
};
136143
}
137144

module/spring-boot-data-redis/src/main/java/org/springframework/boot/data/redis/autoconfigure/PropertiesDataRedisConnectionDetails.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ public Standalone getStandalone() {
9292
return (cluster != null) ? new PropertiesCluster(cluster) : null;
9393
}
9494

95+
@Override
96+
public @Nullable MasterReplica getMasterReplica() {
97+
DataRedisProperties.Masterreplica masterreplica = this.properties.getMasterreplica();
98+
return (masterreplica != null) ? new PropertiesMasterReplica(masterreplica) : null;
99+
}
100+
95101
private @Nullable DataRedisUrl getRedisUrl() {
96102
return DataRedisUrl.of(this.properties.getUrl());
97103
}
@@ -128,6 +134,24 @@ public List<Node> getNodes() {
128134

129135
}
130136

137+
/**
138+
* {@link MasterReplica} implementation backed by properties.
139+
*/
140+
private class PropertiesMasterReplica implements MasterReplica {
141+
142+
private final List<Node> nodes;
143+
144+
PropertiesMasterReplica(DataRedisProperties.Masterreplica properties) {
145+
this.nodes = asNodes(properties.getNodes());
146+
}
147+
148+
@Override
149+
public List<Node> getNodes() {
150+
return this.nodes;
151+
}
152+
153+
}
154+
131155
/**
132156
* {@link Sentinel} implementation backed by properties.
133157
*/

module/spring-boot-data-redis/src/test/java/org/springframework/boot/data/redis/autoconfigure/DataRedisAutoConfigurationJedisTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,15 @@ void testRedisConfigurationWithCluster() {
247247
.isTrue());
248248
}
249249

250+
@Test
251+
void testRedisConfigurationWitMasterReplica() {
252+
this.contextRunner.withPropertyValues("spring.data.redis.masterreplica.nodes=127.0.0.1:27379,127.0.0.1:27380")
253+
.run((context) -> assertThat(context).hasFailed()
254+
.getFailure()
255+
.rootCause()
256+
.hasMessageContaining("'masterReplicaConfig' is not supported by Jedis"));
257+
}
258+
250259
@Test
251260
void testRedisConfigurationWithSslEnabled() {
252261
this.contextRunner.withPropertyValues("spring.data.redis.ssl.enabled:true").run((context) -> {

0 commit comments

Comments
 (0)