From 3b587449a6cfb3f926afdeca99b3b316717f5c57 Mon Sep 17 00:00:00 2001 From: Scott Frederick Date: Wed, 24 Jun 2020 16:45:26 -0500 Subject: [PATCH] Provide cluster info in RedisReactiveHealthIndicator This commit changes the information provided by RedisReactiveHealthIndicator to include cluster details when Spring Data Redis detects that Redis is running in a clustered configuration. This brings the reactive and non-reactive Redis health indicators into alignment. Fixes gh-21514 --- .../actuate/redis/RedisHealthIndicator.java | 13 +++++-- .../redis/RedisReactiveHealthIndicator.java | 36 ++++++++++--------- .../RedisReactiveHealthIndicatorTests.java | 22 +++++++----- 3 files changed, 42 insertions(+), 29 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/redis/RedisHealthIndicator.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/redis/RedisHealthIndicator.java index 83648a9f59..9b2b493dcb 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/redis/RedisHealthIndicator.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/redis/RedisHealthIndicator.java @@ -34,12 +34,19 @@ import org.springframework.util.Assert; * * @author Christian Dupuis * @author Richard Santana + * @author Scott Frederick * @since 2.0.0 */ public class RedisHealthIndicator extends AbstractHealthIndicator { static final String VERSION = "version"; + static final String CLUSTER_SIZE = "cluster_size"; + + static final String SLOTS_UP = "slots_up"; + + static final String SLOTS_FAIL = "slots_fail"; + static final String REDIS_VERSION = "redis_version"; private final RedisConnectionFactory redisConnectionFactory; @@ -56,9 +63,9 @@ public class RedisHealthIndicator extends AbstractHealthIndicator { try { if (connection instanceof RedisClusterConnection) { ClusterInfo clusterInfo = ((RedisClusterConnection) connection).clusterGetClusterInfo(); - builder.up().withDetail("cluster_size", clusterInfo.getClusterSize()) - .withDetail("slots_up", clusterInfo.getSlotsOk()) - .withDetail("slots_fail", clusterInfo.getSlotsFail()); + builder.up().withDetail(CLUSTER_SIZE, clusterInfo.getClusterSize()) + .withDetail(SLOTS_UP, clusterInfo.getSlotsOk()) + .withDetail(SLOTS_FAIL, clusterInfo.getSlotsFail()); } else { Properties info = connection.info(); diff --git a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/redis/RedisReactiveHealthIndicator.java b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/redis/RedisReactiveHealthIndicator.java index c708246320..bcead1de2a 100644 --- a/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/redis/RedisReactiveHealthIndicator.java +++ b/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/redis/RedisReactiveHealthIndicator.java @@ -24,6 +24,7 @@ import reactor.core.scheduler.Schedulers; import org.springframework.boot.actuate.health.AbstractReactiveHealthIndicator; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.ReactiveHealthIndicator; +import org.springframework.data.redis.connection.ClusterInfo; import org.springframework.data.redis.connection.ReactiveRedisClusterConnection; import org.springframework.data.redis.connection.ReactiveRedisConnection; import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory; @@ -52,10 +53,17 @@ public class RedisReactiveHealthIndicator extends AbstractReactiveHealthIndicato } private Mono doHealthCheck(Health.Builder builder, ReactiveRedisConnection connection) { - return connection.serverCommands().info() - .map((info) -> up(builder, info, (connection instanceof ReactiveRedisClusterConnection))) - .onErrorResume((ex) -> Mono.just(down(builder, ex))) - .flatMap((health) -> connection.closeLater().thenReturn(health)); + if (connection instanceof ReactiveRedisClusterConnection) { + ReactiveRedisClusterConnection clusterConnection = (ReactiveRedisClusterConnection) connection; + return clusterConnection.clusterGetClusterInfo().map((info) -> up(builder, info)) + .onErrorResume((ex) -> Mono.just(down(builder, ex))) + .flatMap((health) -> clusterConnection.closeLater().thenReturn(health)); + } + else { + return connection.serverCommands().info().map((info) -> up(builder, info)) + .onErrorResume((ex) -> Mono.just(down(builder, ex))) + .flatMap((health) -> connection.closeLater().thenReturn(health)); + } } private Mono getConnection() { @@ -63,21 +71,15 @@ public class RedisReactiveHealthIndicator extends AbstractReactiveHealthIndicato .subscribeOn(Schedulers.boundedElastic()); } - private Health up(Health.Builder builder, Properties info, boolean isClusterConnection) { - if (isClusterConnection) { - return builder.up().withDetail(RedisHealthIndicator.VERSION, getClusterVersionProperty(info)).build(); - } - else { - return builder.up() - .withDetail(RedisHealthIndicator.VERSION, info.getProperty(RedisHealthIndicator.REDIS_VERSION)) - .build(); - } + private Health up(Health.Builder builder, Properties info) { + return builder.up() + .withDetail(RedisHealthIndicator.VERSION, info.getProperty(RedisHealthIndicator.REDIS_VERSION)).build(); } - private Object getClusterVersionProperty(Properties info) { - return info.keySet().stream().map(String.class::cast) - .filter((key) -> key.endsWith(RedisHealthIndicator.REDIS_VERSION)).findFirst().map(info::get) - .orElse(""); + private Health up(Health.Builder builder, ClusterInfo clusterInfo) { + return builder.up().withDetail(RedisHealthIndicator.CLUSTER_SIZE, clusterInfo.getClusterSize()) + .withDetail(RedisHealthIndicator.SLOTS_UP, clusterInfo.getSlotsOk()) + .withDetail(RedisHealthIndicator.SLOTS_FAIL, clusterInfo.getSlotsFail()).build(); } private Health down(Health.Builder builder, Throwable cause) { diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/redis/RedisReactiveHealthIndicatorTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/redis/RedisReactiveHealthIndicatorTests.java index 078962266d..21ab8f1933 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/redis/RedisReactiveHealthIndicatorTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/redis/RedisReactiveHealthIndicatorTests.java @@ -26,7 +26,7 @@ import reactor.test.StepVerifier; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.Status; import org.springframework.data.redis.RedisConnectionFailureException; -import org.springframework.data.redis.connection.ReactiveClusterServerCommands; +import org.springframework.data.redis.connection.ClusterInfo; import org.springframework.data.redis.connection.ReactiveRedisClusterConnection; import org.springframework.data.redis.connection.ReactiveRedisConnection; import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory; @@ -68,18 +68,22 @@ class RedisReactiveHealthIndicatorTests { @Test void redisClusterIsUp() { - Properties info = new Properties(); - info.put("127.0.0.1:7002.redis_version", "2.8.9"); - ReactiveRedisConnection redisConnection = mock(ReactiveRedisClusterConnection.class); + Properties clusterProperties = new Properties(); + clusterProperties.setProperty("cluster_size", "4"); + clusterProperties.setProperty("cluster_slots_ok", "4"); + clusterProperties.setProperty("cluster_slots_fail", "0"); + ReactiveRedisClusterConnection redisConnection = mock(ReactiveRedisClusterConnection.class); given(redisConnection.closeLater()).willReturn(Mono.empty()); - ReactiveClusterServerCommands commands = mock(ReactiveClusterServerCommands.class); - given(commands.info()).willReturn(Mono.just(info)); - RedisReactiveHealthIndicator healthIndicator = createHealthIndicator(redisConnection, commands); + given(redisConnection.clusterGetClusterInfo()).willReturn(Mono.just(new ClusterInfo(clusterProperties))); + ReactiveRedisConnectionFactory redisConnectionFactory = mock(ReactiveRedisConnectionFactory.class); + given(redisConnectionFactory.getReactiveConnection()).willReturn(redisConnection); + RedisReactiveHealthIndicator healthIndicator = new RedisReactiveHealthIndicator(redisConnectionFactory); Mono health = healthIndicator.health(); StepVerifier.create(health).consumeNextWith((h) -> { assertThat(h.getStatus()).isEqualTo(Status.UP); - assertThat(h.getDetails()).containsOnlyKeys("version"); - assertThat(h.getDetails().get("version")).isEqualTo("2.8.9"); + assertThat(h.getDetails().get("cluster_size")).isEqualTo(4L); + assertThat(h.getDetails().get("slots_up")).isEqualTo(4L); + assertThat(h.getDetails().get("slots_fail")).isEqualTo(0L); }).verifyComplete(); verify(redisConnection).closeLater(); }