From 3c43871223315fdcc57f4dc888fbc993bc95b53a Mon Sep 17 00:00:00 2001 From: Manyanda Chitimbo Date: Tue, 20 Oct 2020 18:11:19 +0200 Subject: [PATCH] feat(redis): check Reactive Client for healthcheck Follows up https://github.com/quarkusio/quarkus/pull/12781#discussion_r508067467 --- .../client/runtime/RedisAPIProducer.java | 14 ++-- .../runtime/health/RedisHealthCheck.java | 75 +++++++++++++++---- .../it/RedisWithNamedClientResource.java | 2 +- .../src/main/resources/application.properties | 1 + .../io/quarkus/redis/it/HealthCheckTest.java | 4 + .../it/QuarkusRedisWithNamedClientIT.java | 2 +- ...a => QuarkusRedisWithNamedClientTest.java} | 2 +- 7 files changed, 77 insertions(+), 23 deletions(-) rename integration-tests/redis-client/src/test/java/io/quarkus/redis/it/{QuarkusRedisWithNamedTest.java => QuarkusRedisWithNamedClientTest.java} (97%) diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/client/runtime/RedisAPIProducer.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/client/runtime/RedisAPIProducer.java index a24eed4c8007b..08d5462bad939 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/client/runtime/RedisAPIProducer.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/client/runtime/RedisAPIProducer.java @@ -8,6 +8,7 @@ import io.quarkus.redis.client.RedisClient; import io.quarkus.redis.client.reactive.ReactiveRedisClient; +import io.quarkus.redis.client.runtime.RedisConfig.RedisConfiguration; import io.vertx.core.Vertx; import io.vertx.redis.client.Redis; import io.vertx.redis.client.RedisAPI; @@ -17,10 +18,10 @@ class RedisAPIProducer { private static Map REDIS_APIS = new ConcurrentHashMap<>(); private final Vertx vertx; - private final RedisConfig redisRuntimeConfig; + private final RedisConfig redisConfig; public RedisAPIProducer(RedisConfig redisConfig, Vertx vertx) { - this.redisRuntimeConfig = redisConfig; + this.redisConfig = redisConfig; this.vertx = vertx; } @@ -29,11 +30,12 @@ public RedisAPIContainer getRedisAPIContainer(String name) { @Override public RedisAPIContainer apply(String s) { long timeout = 10; - RedisConfig.RedisConfiguration redisConfig = RedisClientUtil.getConfiguration(redisRuntimeConfig, name); - if (redisConfig.timeout.isPresent()) { - timeout = redisConfig.timeout.get().getSeconds(); + RedisConfiguration redisConfiguration = RedisClientUtil.getConfiguration(RedisAPIProducer.this.redisConfig, + name); + if (redisConfiguration.timeout.isPresent()) { + timeout = redisConfiguration.timeout.get().getSeconds(); } - RedisOptions options = RedisClientUtil.buildOptions(redisConfig); + RedisOptions options = RedisClientUtil.buildOptions(redisConfiguration); Redis redis = Redis.createClient(vertx, options); RedisAPI redisAPI = RedisAPI.api(redis); MutinyRedis mutinyRedis = new MutinyRedis(redis); diff --git a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/client/runtime/health/RedisHealthCheck.java b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/client/runtime/health/RedisHealthCheck.java index eb62ce0700031..e54041a525cab 100644 --- a/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/client/runtime/health/RedisHealthCheck.java +++ b/extensions/redis-client/runtime/src/main/java/io/quarkus/redis/client/runtime/health/RedisHealthCheck.java @@ -2,13 +2,14 @@ import static io.quarkus.redis.client.runtime.RedisClientUtil.DEFAULT_CLIENT; +import java.time.Duration; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.Set; import javax.annotation.PostConstruct; import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Any; import javax.enterprise.inject.spi.Bean; import org.eclipse.microprofile.health.HealthCheck; @@ -17,41 +18,87 @@ import org.eclipse.microprofile.health.Readiness; import io.quarkus.arc.Arc; +import io.quarkus.arc.InstanceHandle; import io.quarkus.redis.client.RedisClient; +import io.quarkus.redis.client.RedisClientName; +import io.quarkus.redis.client.reactive.ReactiveRedisClient; +import io.quarkus.redis.client.runtime.RedisClientUtil; +import io.quarkus.redis.client.runtime.RedisConfig; +import io.quarkus.redis.client.runtime.RedisConfig.RedisConfiguration; import io.vertx.redis.client.Response; @Readiness @ApplicationScoped class RedisHealthCheck implements HealthCheck { - private Map clients = new HashMap<>(); + private final Map clients = new HashMap<>(); + private final Map reactiveClients = new HashMap<>(); + private final RedisConfig redisConfig; + + public RedisHealthCheck(RedisConfig redisConfig) { + this.redisConfig = redisConfig; + } @PostConstruct protected void init() { - Set> beans = Arc.container().beanManager().getBeans(RedisClient.class); - for (Bean bean : beans) { - if (bean.getName() == null) { - // this is the default redis client: retrieve it by type - RedisClient defaultClient = Arc.container().instance(RedisClient.class).get(); - clients.put(DEFAULT_CLIENT, defaultClient); - } else { - RedisClient client = (RedisClient) Arc.container().instance(bean.getName()).get(); - clients.put(bean.getName(), client); + for (InstanceHandle handle : Arc.container().select(RedisClient.class, Any.Literal.INSTANCE).handles()) { + String clientName = getClientName(handle.getBean()); + clients.put(clientName == null ? DEFAULT_CLIENT : clientName, handle.get()); + } + + for (InstanceHandle handle : Arc.container() + .select(ReactiveRedisClient.class, Any.Literal.INSTANCE).handles()) { + String clientName = getClientName(handle.getBean()); + reactiveClients.put(clientName == null ? DEFAULT_CLIENT : clientName, handle.get()); + } + } + + private String getClientName(Bean bean) { + for (Object qualifier : bean.getQualifiers()) { + if (qualifier instanceof RedisClientName) { + return ((RedisClientName) qualifier).value(); } } + return null; } @Override public HealthCheckResponse call() { HealthCheckResponseBuilder builder = HealthCheckResponse.named("Redis connection health check").up(); for (Map.Entry client : clients.entrySet()) { - boolean isDefault = DEFAULT_CLIENT.equals(client.getKey()); - RedisClient redisClient = client.getValue(); try { + boolean isDefault = DEFAULT_CLIENT.equals(client.getKey()); + RedisClient redisClient = client.getValue(); String redisClientName = isDefault ? "default" : client.getKey(); Response response = redisClient.ping(Collections.emptyList()); builder.up().withData(redisClientName, response.toString()); } catch (Exception e) { - return builder.down().withData("reason", e.getMessage()).build(); + return builder.down().withData("reason", "client [" + client.getKey() + "]: " + e.getMessage()).build(); + } + } + + for (Map.Entry client : reactiveClients.entrySet()) { + + // Ignore named ReactiveRedisClient that have a blocking RedisClient since they have already been checked as part of blocking clients + if (clients.containsKey(client.getKey())) { + continue; + } + + try { + boolean isDefault = DEFAULT_CLIENT.equals(client.getKey()); + ReactiveRedisClient redisClient = client.getValue(); + RedisConfiguration redisConfig = RedisClientUtil.getConfiguration(this.redisConfig, + isDefault ? DEFAULT_CLIENT : client.getKey()); + long timeout = 10; + if (redisConfig.timeout.isPresent()) { + timeout = redisConfig.timeout.get().getSeconds(); + } + String redisClientName = isDefault ? "default" : client.getKey(); + io.vertx.mutiny.redis.client.Response response = redisClient.ping(Collections.emptyList()).await() + .atMost(Duration.ofSeconds(timeout)); + builder.up().withData(redisClientName, response.toString()); + } catch (Exception e) { + return builder.down().withData("reason", "client [" + client.getKey() + "]: " + e.getMessage()) + .build(); } } return builder.build(); diff --git a/integration-tests/redis-client/src/main/java/io/quarkus/redis/it/RedisWithNamedClientResource.java b/integration-tests/redis-client/src/main/java/io/quarkus/redis/it/RedisWithNamedClientResource.java index 86891ad51537a..fe5e1826122b1 100644 --- a/integration-tests/redis-client/src/main/java/io/quarkus/redis/it/RedisWithNamedClientResource.java +++ b/integration-tests/redis-client/src/main/java/io/quarkus/redis/it/RedisWithNamedClientResource.java @@ -23,7 +23,7 @@ public class RedisWithNamedClientResource { RedisClient redisClient; @Inject - @RedisClientName("named-client") + @RedisClientName("named-reactive-client") ReactiveRedisClient reactiveRedisClient; // synchronous diff --git a/integration-tests/redis-client/src/main/resources/application.properties b/integration-tests/redis-client/src/main/resources/application.properties index 22460ebee4b78..f8e6dad4d568c 100644 --- a/integration-tests/redis-client/src/main/resources/application.properties +++ b/integration-tests/redis-client/src/main/resources/application.properties @@ -1,4 +1,5 @@ quarkus.redis.hosts=redis://localhost:6379/0 quarkus.redis.named-client.hosts=redis://localhost:6379/1 quarkus.redis.parameter-injection.hosts=redis://localhost:6379/2 +quarkus.redis.named-reactive-client.hosts=redis://localhost:6379/1 quarkus.native.additional-build-args=-H:+TraceClassInitialization diff --git a/integration-tests/redis-client/src/test/java/io/quarkus/redis/it/HealthCheckTest.java b/integration-tests/redis-client/src/test/java/io/quarkus/redis/it/HealthCheckTest.java index c3c424056d424..b1f891ff48da1 100644 --- a/integration-tests/redis-client/src/test/java/io/quarkus/redis/it/HealthCheckTest.java +++ b/integration-tests/redis-client/src/test/java/io/quarkus/redis/it/HealthCheckTest.java @@ -3,6 +3,7 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.hasKey; import org.junit.jupiter.api.Test; @@ -19,6 +20,9 @@ public void testHealthCheck() { .header("Content-Type", containsString("charset=UTF-8")) .body("status", is("UP"), "checks.status", containsInAnyOrder("UP"), + "checks.data", containsInAnyOrder(hasKey("default")), + "checks.data", containsInAnyOrder(hasKey("named-client")), + "checks.data", containsInAnyOrder(hasKey("named-reactive-client")), "checks.name", containsInAnyOrder("Redis connection health check")); } } diff --git a/integration-tests/redis-client/src/test/java/io/quarkus/redis/it/QuarkusRedisWithNamedClientIT.java b/integration-tests/redis-client/src/test/java/io/quarkus/redis/it/QuarkusRedisWithNamedClientIT.java index 25a3606e1d9d2..92c551b40c99e 100644 --- a/integration-tests/redis-client/src/test/java/io/quarkus/redis/it/QuarkusRedisWithNamedClientIT.java +++ b/integration-tests/redis-client/src/test/java/io/quarkus/redis/it/QuarkusRedisWithNamedClientIT.java @@ -3,6 +3,6 @@ import io.quarkus.test.junit.NativeImageTest; @NativeImageTest -class QuarkusRedisWithNamedClientIT extends QuarkusRedisWithNamedTest { +class QuarkusRedisWithNamedClientIT extends QuarkusRedisWithNamedClientTest { } diff --git a/integration-tests/redis-client/src/test/java/io/quarkus/redis/it/QuarkusRedisWithNamedTest.java b/integration-tests/redis-client/src/test/java/io/quarkus/redis/it/QuarkusRedisWithNamedClientTest.java similarity index 97% rename from integration-tests/redis-client/src/test/java/io/quarkus/redis/it/QuarkusRedisWithNamedTest.java rename to integration-tests/redis-client/src/test/java/io/quarkus/redis/it/QuarkusRedisWithNamedClientTest.java index d15c301a5563b..bdde0097d9bcb 100644 --- a/integration-tests/redis-client/src/test/java/io/quarkus/redis/it/QuarkusRedisWithNamedTest.java +++ b/integration-tests/redis-client/src/test/java/io/quarkus/redis/it/QuarkusRedisWithNamedClientTest.java @@ -7,7 +7,7 @@ import io.restassured.RestAssured; @QuarkusTest -class QuarkusRedisWithNamedTest { +class QuarkusRedisWithNamedClientTest { static final String SYNC_KEY = "named-sync-key"; static final String SYNC_VALUE = "named-sync-value";