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
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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<Health> 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<ReactiveRedisConnection> 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) {
|
||||
|
||||
@@ -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> 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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user