Skip to content

Commit b273037

Browse files
Fix RedisReactiveHealthIndicator in clustered configuration
Prior to Spring Data Redis version 2.2.8, the contents of the Properties object returned from the ReactiveRedisConnection.ServerCommands.info API were the same for clustered and non-clustered Redis configurations, containing a set of key/value pairs. This allowed ReactiveRedisHealthIndicator to get a version property using a well-known key. Starting with Spring Data Redis 2.2.8, the info property keys contain a host:port prefix in a clustered Redis configuration. This prevented ReactiveRedisHealthIndicator from getting the version property as before and resulted in the health always being reported as DOWN. This commit adjusts ReactiveRedisHealthIndicator to detect the clustered configuration from Spring Data Redis and find the version property for one of the reported cluster nodes. Fixes gh-22061
1 parent 3fed27f commit b273037

File tree

2 files changed

+40
-4
lines changed

2 files changed

+40
-4
lines changed

spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/redis/RedisReactiveHealthIndicator.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.springframework.boot.actuate.health.AbstractReactiveHealthIndicator;
2525
import org.springframework.boot.actuate.health.Health;
2626
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
27+
import org.springframework.data.redis.connection.ReactiveRedisClusterConnection;
2728
import org.springframework.data.redis.connection.ReactiveRedisConnection;
2829
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
2930

@@ -33,6 +34,7 @@
3334
* @author Stephane Nicoll
3435
* @author Mark Paluch
3536
* @author Artsiom Yudovin
37+
* @author Scott Frederick
3638
* @since 2.0.0
3739
*/
3840
public class RedisReactiveHealthIndicator extends AbstractReactiveHealthIndicator {
@@ -50,7 +52,8 @@ protected Mono<Health> doHealthCheck(Health.Builder builder) {
5052
}
5153

5254
private Mono<Health> doHealthCheck(Health.Builder builder, ReactiveRedisConnection connection) {
53-
return connection.serverCommands().info().map((info) -> up(builder, info))
55+
return connection.serverCommands().info()
56+
.map((info) -> up(builder, info, (connection instanceof ReactiveRedisClusterConnection)))
5457
.onErrorResume((ex) -> Mono.just(down(builder, ex)))
5558
.flatMap((health) -> connection.closeLater().thenReturn(health));
5659
}
@@ -60,9 +63,21 @@ private Mono<ReactiveRedisConnection> getConnection() {
6063
.subscribeOn(Schedulers.boundedElastic());
6164
}
6265

63-
private Health up(Health.Builder builder, Properties info) {
64-
return builder.up()
65-
.withDetail(RedisHealthIndicator.VERSION, info.getProperty(RedisHealthIndicator.REDIS_VERSION)).build();
66+
private Health up(Health.Builder builder, Properties info, boolean isClusterConnection) {
67+
if (isClusterConnection) {
68+
return builder.up().withDetail(RedisHealthIndicator.VERSION, getClusterVersionProperty(info)).build();
69+
}
70+
else {
71+
return builder.up()
72+
.withDetail(RedisHealthIndicator.VERSION, info.getProperty(RedisHealthIndicator.REDIS_VERSION))
73+
.build();
74+
}
75+
}
76+
77+
private Object getClusterVersionProperty(Properties info) {
78+
return info.keySet().stream().map(String.class::cast)
79+
.filter((key) -> key.endsWith(RedisHealthIndicator.REDIS_VERSION)).findFirst().map(info::get)
80+
.orElse("");
6681
}
6782

6883
private Health down(Health.Builder builder, Throwable cause) {

spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/redis/RedisReactiveHealthIndicatorTests.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import org.springframework.boot.actuate.health.Health;
2727
import org.springframework.boot.actuate.health.Status;
2828
import org.springframework.data.redis.RedisConnectionFailureException;
29+
import org.springframework.data.redis.connection.ReactiveClusterServerCommands;
30+
import org.springframework.data.redis.connection.ReactiveRedisClusterConnection;
2931
import org.springframework.data.redis.connection.ReactiveRedisConnection;
3032
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
3133
import org.springframework.data.redis.connection.ReactiveServerCommands;
@@ -42,6 +44,7 @@
4244
* @author Mark Paluch
4345
* @author Nikolay Rybak
4446
* @author Artsiom Yudovin
47+
* @author Scott Frederick
4548
*/
4649
class RedisReactiveHealthIndicatorTests {
4750

@@ -63,6 +66,24 @@ void redisIsUp() {
6366
verify(redisConnection).closeLater();
6467
}
6568

69+
@Test
70+
void redisClusterIsUp() {
71+
Properties info = new Properties();
72+
info.put("127.0.0.1:7002.redis_version", "2.8.9");
73+
ReactiveRedisConnection redisConnection = mock(ReactiveRedisClusterConnection.class);
74+
given(redisConnection.closeLater()).willReturn(Mono.empty());
75+
ReactiveClusterServerCommands commands = mock(ReactiveClusterServerCommands.class);
76+
given(commands.info()).willReturn(Mono.just(info));
77+
RedisReactiveHealthIndicator healthIndicator = createHealthIndicator(redisConnection, commands);
78+
Mono<Health> health = healthIndicator.health();
79+
StepVerifier.create(health).consumeNextWith((h) -> {
80+
assertThat(h.getStatus()).isEqualTo(Status.UP);
81+
assertThat(h.getDetails()).containsOnlyKeys("version");
82+
assertThat(h.getDetails().get("version")).isEqualTo("2.8.9");
83+
}).verifyComplete();
84+
verify(redisConnection).closeLater();
85+
}
86+
6687
@Test
6788
void redisCommandIsDown() {
6889
ReactiveServerCommands commands = mock(ReactiveServerCommands.class);

0 commit comments

Comments
 (0)