jdbcDataSources) {
for (JdbcDataSourceBuildItem jdbcDataSource : jdbcDataSources) {
if (jdbcDataSource.isDefault()) {
diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HealthPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HealthPropertyMappers.java
index 75186e88eb..124a7ab382 100644
--- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HealthPropertyMappers.java
+++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HealthPropertyMappers.java
@@ -14,8 +14,6 @@ final class HealthPropertyMappers {
fromOption(HealthOptions.HEALTH_ENABLED)
.to("quarkus.health.extensions.enabled")
.paramLabel(Boolean.TRUE + "|" + Boolean.FALSE)
- .build(),
- fromOption(HealthOptions.HEALTH_CLASSIC_PROBES_ENABLED)
.build()
};
}
diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/health/ReactiveHealthHandler.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/health/ReactiveHealthHandler.java
deleted file mode 100644
index 7d3d98a198..0000000000
--- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/health/ReactiveHealthHandler.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2023 Red Hat, Inc. and/or its affiliates
- * and other contributors as indicated by the @author tags.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.keycloak.quarkus.runtime.integration.health;
-
-import io.quarkus.smallrye.health.runtime.SmallRyeLivenessHandler;
-import io.smallrye.health.SmallRyeHealth;
-import io.smallrye.health.SmallRyeHealthReporter;
-import io.smallrye.mutiny.Uni;
-import io.vertx.core.Handler;
-import io.vertx.ext.web.RoutingContext;
-
-/**
- * This adds the possibility to have a non-blocking health handler in Quarkus.
- *
- * Without a non-blocking health check, all liveness and readiness probes will enqueue in the worker thread pool. Under high load
- * of if there is a lot of blocking IO happening (for example, during Keycloak cluster rebalancing), this leads to probes being queued.
- * Queued probes would lead to timeouts unless the timeouts are configured to 10-20 seconds. Reactive probes avoid the enqueueing
- * in the worker thread pool for all non-blocking probes, which will be the default for the (otherwise empty) liveness probe.
- * For the readiness probe, this depends on the implementation of the specific readiness probes.
- *
- * This is a workaround until quarkusio/quarkus#35100 is available
- * in a regular Quarkus version. Then these classes can be removed.
- *
- * @author Alexander Schwartz
- */
-public abstract class ReactiveHealthHandler implements Handler {
-
- @Override
- public void handle(RoutingContext context) {
- Uni health = getHealth();
- health.subscribe().with(smallRyeHealth -> {
- new SmallRyeLivenessHandler() {
- @Override
- protected SmallRyeHealth getHealth(SmallRyeHealthReporter reporter, RoutingContext ctx) {
- return smallRyeHealth;
- }
- }.handle(context);
- });
- }
-
- protected abstract Uni getHealth();
-}
diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/health/ReactiveLivenessHandler.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/health/ReactiveLivenessHandler.java
deleted file mode 100644
index c5e5c6db4d..0000000000
--- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/health/ReactiveLivenessHandler.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2023 Red Hat, Inc. and/or its affiliates
- * and other contributors as indicated by the @author tags.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.keycloak.quarkus.runtime.integration.health;
-
-import io.quarkus.arc.Arc;
-import io.smallrye.health.SmallRyeHealth;
-import io.smallrye.health.SmallRyeHealthReporter;
-import io.smallrye.mutiny.Uni;
-
-/**
- * @author Alexander Schwartz
- */
-public class ReactiveLivenessHandler extends ReactiveHealthHandler {
-
- @Override
- protected Uni getHealth() {
- SmallRyeHealthReporter healthReporter = Arc.container().instance(SmallRyeHealthReporter.class).get();
- return healthReporter.getLivenessAsync();
- }
-}
diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/health/ReactiveReadinessHandler.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/health/ReactiveReadinessHandler.java
deleted file mode 100644
index 5900a2cbbd..0000000000
--- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/health/ReactiveReadinessHandler.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2023 Red Hat, Inc. and/or its affiliates
- * and other contributors as indicated by the @author tags.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.keycloak.quarkus.runtime.integration.health;
-
-import io.quarkus.arc.Arc;
-import io.smallrye.health.SmallRyeHealth;
-import io.smallrye.health.SmallRyeHealthReporter;
-import io.smallrye.mutiny.Uni;
-
-/**
- * @author Alexander Schwartz
- */
-public class ReactiveReadinessHandler extends ReactiveHealthHandler {
-
- @Override
- protected Uni getHealth() {
- SmallRyeHealthReporter healthReporter = Arc.container().instance(SmallRyeHealthReporter.class).get();
- return healthReporter.getReadinessAsync();
- }
-}
diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/health/KeycloakReadyAsyncHealthCheck.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/health/KeycloakReadyAsyncHealthCheck.java
deleted file mode 100644
index 8626422431..0000000000
--- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/health/KeycloakReadyAsyncHealthCheck.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2020 Red Hat, Inc. and/or its affiliates
- * and other contributors as indicated by the @author tags.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.keycloak.quarkus.runtime.services.health;
-
-import io.agroal.api.AgroalDataSource;
-import io.quarkus.agroal.runtime.health.DataSourceHealthCheck;
-import io.quarkus.smallrye.health.runtime.QuarkusAsyncHealthCheckFactory;
-import io.smallrye.health.api.AsyncHealthCheck;
-import io.smallrye.mutiny.Uni;
-import jakarta.annotation.PostConstruct;
-import jakarta.enterprise.context.ApplicationScoped;
-import jakarta.inject.Inject;
-import org.eclipse.microprofile.health.HealthCheckResponse;
-import org.eclipse.microprofile.health.HealthCheckResponseBuilder;
-import org.eclipse.microprofile.health.Readiness;
-
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.format.DateTimeFormatter;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * Keycloak Healthcheck Readiness Probe.
- *
- * Performs a hybrid between the passive and the active mode. If there are no healthy connections in the pool,
- * it invokes the standard DataSourceHealthCheck
that creates a new connection and checks if it's valid.
- *
- * While the check for healthy connections is non-blocking, the standard check is blocking, so it needs to be wrapped.
- *
- * When NON_BLOCKING_PROBES is no longer behind a feature flag but the only option, it will replace the
- * {@link KeycloakReadyHealthCheck}.
- *
- * @see Healthcheck API Design
- */
-@Readiness
-@ApplicationScoped
-public class KeycloakReadyAsyncHealthCheck implements AsyncHealthCheck {
-
- /** As the DataSourceHealthCheck doesn't exist as an application scoped bean,
- * create our own instance here which exposes the init()
call for the delegate. */
- MyDataSourceHealthCheck delegate;
-
- private static class MyDataSourceHealthCheck extends DataSourceHealthCheck {
- @Override
- public void init() {
- super.init();
- }
- }
-
- @PostConstruct
- protected void init() {
- delegate = new MyDataSourceHealthCheck();
- delegate.init();
- }
-
- /**
- * Date formatter, the same as used by Quarkus. This enables users to quickly compare the date printed
- * by the probe with the logs.
- */
- static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss,SSS").withZone(ZoneId.systemDefault());
-
- @Inject
- AgroalDataSource agroalDataSource;
-
- @Inject
- QuarkusAsyncHealthCheckFactory healthCheckFactory;
-
- AtomicReference failingSince = new AtomicReference<>();
-
- @Override
- public Uni call() {
- HealthCheckResponseBuilder builder = HealthCheckResponse.named("Keycloak database connections async health check").up();
- long activeCount = agroalDataSource.getMetrics().activeCount();
- long invalidCount = agroalDataSource.getMetrics().invalidCount();
- if (activeCount < 1 || invalidCount > 0) {
- return healthCheckFactory.callSync(() -> {
- HealthCheckResponse activeCheckResult = delegate.call();
- if (activeCheckResult.getStatus() == HealthCheckResponse.Status.DOWN) {
- builder.down();
- Instant failingTime = failingSince.updateAndGet(this::createInstanceIfNeeded);
- builder.withData("Failing since", DATE_FORMATTER.format(failingTime));
- }
- return builder.build();
- });
- } else {
- failingSince.set(null);
- return healthCheckFactory.callAsync(() -> Uni.createFrom().item(builder.build()));
- }
- }
-
- Instant createInstanceIfNeeded(Instant instant) {
- if (instant == null) {
- return Instant.now();
- }
- return instant;
- }
-}
\ No newline at end of file
diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/health/KeycloakReadyHealthCheck.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/health/KeycloakReadyHealthCheck.java
index d2883eaccf..7d02624063 100644
--- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/health/KeycloakReadyHealthCheck.java
+++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/health/KeycloakReadyHealthCheck.java
@@ -18,12 +18,15 @@ package org.keycloak.quarkus.runtime.services.health;
import io.agroal.api.AgroalDataSource;
import io.quarkus.agroal.runtime.health.DataSourceHealthCheck;
+import io.quarkus.smallrye.health.runtime.QuarkusAsyncHealthCheckFactory;
+import io.smallrye.health.api.AsyncHealthCheck;
+import io.smallrye.mutiny.Uni;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.HealthCheckResponseBuilder;
import org.eclipse.microprofile.health.Readiness;
-import jakarta.enterprise.context.ApplicationScoped;
-import jakarta.inject.Inject;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
@@ -31,15 +34,17 @@ import java.util.concurrent.atomic.AtomicReference;
/**
* Keycloak Healthcheck Readiness Probe.
- *
+ *
* Performs a hybrid between the passive and the active mode. If there are no healthy connections in the pool,
- * it invokes the standard DataSourceHealthCheck
that creates a new connection and checks if its valid.
+ * it invokes the standard DataSourceHealthCheck
that creates a new connection and checks if it's valid.
+ *
+ * While the check for healthy connections is non-blocking, the standard check is blocking, so it needs to be wrapped.
*
* @see Healthcheck API Design
*/
@Readiness
@ApplicationScoped
-public class KeycloakReadyHealthCheck extends DataSourceHealthCheck {
+public class KeycloakReadyHealthCheck implements AsyncHealthCheck {
/**
* Date formatter, the same as used by Quarkus. This enables users to quickly compare the date printed
@@ -50,24 +55,33 @@ public class KeycloakReadyHealthCheck extends DataSourceHealthCheck {
@Inject
AgroalDataSource agroalDataSource;
+ @Inject
+ QuarkusAsyncHealthCheckFactory healthCheckFactory;
+
+ @Inject
+ DataSourceHealthCheck dataSourceHealthCheck;
+
AtomicReference failingSince = new AtomicReference<>();
@Override
- public HealthCheckResponse call() {
- HealthCheckResponseBuilder builder = HealthCheckResponse.named("Keycloak database connections health check").up();
+ public Uni call() {
+ HealthCheckResponseBuilder builder = HealthCheckResponse.named("Keycloak database connections async health check").up();
long activeCount = agroalDataSource.getMetrics().activeCount();
long invalidCount = agroalDataSource.getMetrics().invalidCount();
if (activeCount < 1 || invalidCount > 0) {
- HealthCheckResponse activeCheckResult = super.call();
- if (activeCheckResult.getStatus() == HealthCheckResponse.Status.DOWN) {
- builder.down();
- Instant failingTime = failingSince.updateAndGet(this::createInstanceIfNeeded);
- builder.withData("Failing since", DATE_FORMATTER.format(failingTime));
- }
+ return healthCheckFactory.callSync(() -> {
+ HealthCheckResponse activeCheckResult = dataSourceHealthCheck.call();
+ if (activeCheckResult.getStatus() == HealthCheckResponse.Status.DOWN) {
+ builder.down();
+ Instant failingTime = failingSince.updateAndGet(this::createInstanceIfNeeded);
+ builder.withData("Failing since", DATE_FORMATTER.format(failingTime));
+ }
+ return builder.build();
+ });
} else {
failingSince.set(null);
+ return healthCheckFactory.callAsync(() -> Uni.createFrom().item(builder.build()));
}
- return builder.build();
}
Instant createInstanceIfNeeded(Instant instant) {
diff --git a/quarkus/runtime/src/main/resources/application.properties b/quarkus/runtime/src/main/resources/application.properties
index c369cf48d2..9e9fd36613 100644
--- a/quarkus/runtime/src/main/resources/application.properties
+++ b/quarkus/runtime/src/main/resources/application.properties
@@ -7,7 +7,6 @@ quarkus.banner.enabled=false
# Disable health checks from extensions, since we provide our own (default is true)
quarkus.health.extensions.enabled=false
-quarkus.datasource.health.enabled=false
# Disable http metrics binder as URL parameters are only shown with placeholders for '/resource' URLs, but not
# for '/admin' and '/realms'. Neither the IDs of entities nor the realm name should be part of the metric names
diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HealthDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HealthDistTest.java
index 20134a4a3e..2c83578e62 100644
--- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HealthDistTest.java
+++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HealthDistTest.java
@@ -80,19 +80,6 @@ public class HealthDistTest {
.statusCode(404);
}
- @Test
- @Launch({ "start-dev", "--health-enabled=true", "--metrics-enabled=true", "--health-classic-probes-enabled=true" })
- void testBlockingProbes() {
- when().get("/health/live").then()
- .statusCode(200);
- when().get("/health/ready").then()
- .statusCode(200)
- .body("checks[0].name", equalTo("Keycloak database connections health check"))
- .body("checks.size()", equalTo(1));
- when().get("/lb-check").then()
- .statusCode(404);
- }
-
@Test
void testUsingRelativePath(KeycloakDistribution distribution) {
for (String relativePath : List.of("/auth", "/auth/", "auth")) {
diff --git a/testsuite/utils/pom.xml b/testsuite/utils/pom.xml
index 96cf6d67e9..8f11485ab1 100755
--- a/testsuite/utils/pom.xml
+++ b/testsuite/utils/pom.xml
@@ -266,6 +266,7 @@
mysql
mysql-connector-java
+ ${mysql-jdbc.version}
compile