From 4665cceeb33b47739f2e7444c9e26ddbf37b908e Mon Sep 17 00:00:00 2001 From: a Date: Fri, 12 Jun 2026 22:59:18 +0100 Subject: [PATCH] Make app boot without Redis: gate Valkey beans at build time, disable redis health, default OIDC off --- .../src/main/resources/application.properties | 16 +++++++++++++++- .../cluster/valkey/ValkeyClusterBackplane.java | 13 ++++++------- .../valkey/ValkeyConnectionConfiguration.java | 10 +++++++--- .../cluster/valkey/ValkeyDistributedLock.java | 7 ++++--- .../cluster/valkey/ValkeyInstanceRegistry.java | 8 ++++---- .../cluster/valkey/ValkeyJobStore.java | 6 ++++++ .../cluster/valkey/ValkeyKeyValueCache.java | 4 ++++ .../cluster/valkey/ValkeyRateLimitStore.java | 12 ++++++------ 8 files changed, 52 insertions(+), 24 deletions(-) diff --git a/app/core/src/main/resources/application.properties b/app/core/src/main/resources/application.properties index 1806a6a25b..939d9af03f 100644 --- a/app/core/src/main/resources/application.properties +++ b/app/core/src/main/resources/application.properties @@ -50,6 +50,14 @@ quarkus.http.so-reuse-port=false # - spring.web.resources.mime-mappings.webmanifest=application/manifest+json # - server.servlet.session.tracking-modes=cookie (configure on quarkus-undertow) +# ---- OIDC / OAuth2 login ----------------------------------------------------------------------- +# The quarkus-oidc extension is on the classpath for OAuth2/OIDC login, but it FAILS STARTUP if +# enabled without quarkus.oidc.auth-server-url. Under Spring, OAuth2 login was activated only when +# the operator configured a provider; the default (and the login-disabled / ultra-lite deployment) +# ran with no OIDC at all. Mirror that: OIDC is off by default and an OAuth2 deployment re-enables +# it by setting quarkus.oidc.enabled=true plus quarkus.oidc.auth-server-url/client-id/... . +quarkus.oidc.enabled=false + # ---- Error handling (was spring.web.error.* / spring.mvc.problemdetails.enabled=false) -------- # TODO: Migration required - GlobalExceptionHandler is an @ControllerAdvice; rewrite as JAX-RS # ExceptionMapper(s) producing RFC 7807 ProblemDetail responses. The Spring error-page / whitelabel @@ -113,6 +121,12 @@ v2=true # spring.config.additional-location. Quarkus uses a different config-source mechanism # (SmallRye Config / quarkus.config.locations). Port ConfigInitializer accordingly. -# ---- Redis auto-config exclusion ------------------------------------------------------------- +# ---- Redis / Valkey backplane ---------------------------------------------------------------- # spring.autoconfigure.exclude (Redis) -> not applicable; Quarkus only wires the Redis client when # quarkus.redis.* is configured, so no dead localhost:6379 factory is created by default. +# The Redis client is only used by the optional Valkey cluster backplane (cluster.enabled=true AND +# cluster.backplane=valkey). In the default single-node deployment no quarkus.redis.hosts is set, so +# the RedisDataSource bean is INACTIVE. The Redis readiness health check is a startup observer that +# eagerly resolves that inactive bean and aborts boot - disable it here; a Valkey deployment that +# configures quarkus.redis.hosts can re-enable it with quarkus.redis.health.enabled=true. +quarkus.redis.health.enabled=false diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyClusterBackplane.java b/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyClusterBackplane.java index c5f45c6d31..c871261427 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyClusterBackplane.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyClusterBackplane.java @@ -1,5 +1,6 @@ package stirling.software.proprietary.cluster.valkey; +import io.quarkus.arc.properties.IfBuildProperty; import io.quarkus.redis.datasource.RedisDataSource; import jakarta.enterprise.context.ApplicationScoped; @@ -10,15 +11,13 @@ import lombok.extern.slf4j.Slf4j; import stirling.software.common.cluster.ClusterBackplane; import stirling.software.common.model.ApplicationProperties; +// Build-time gating: this bean (and its RedisDataSource dependency) is only included in the build +// when cluster.backplane=valkey. With the default backplane (inprocess) the whole Valkey bean is +// removed, so RedisDataSource has no consumers and Quarkus emits no eager startup observer for the +// inactive Redis client - the in-process @DefaultBean ClusterBackplane satisfies the interface. @Slf4j +@IfBuildProperty(name = "cluster.backplane", stringValue = "valkey") @ApplicationScoped -// TODO: Migration required - @ConditionalOnValkeyBackplane was a Spring @ConditionalOnExpression -// guard ("cluster.enabled=true AND cluster.backplane=valkey"). Quarkus has no runtime -// @Conditional for beans; this bean is now always instantiated. Gate selection at runtime -// (e.g. a ClusterBackplane producer that picks valkey vs in-process based on injected config), -// or use @io.quarkus.arc.lookup.LookupIfProperty(name="cluster.backplane", stringValue="valkey") -// (build-time/static only - does not also check cluster.enabled). The composite condition must be -// re-expressed accordingly. public class ValkeyClusterBackplane implements ClusterBackplane { @Inject ApplicationProperties applicationProperties; diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyConnectionConfiguration.java b/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyConnectionConfiguration.java index 5e27f4eaf2..34b2f47f8e 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyConnectionConfiguration.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyConnectionConfiguration.java @@ -6,7 +6,7 @@ import java.time.Duration; import io.lettuce.core.RedisClient; import io.lettuce.core.RedisURI; -import io.quarkus.arc.lookup.LookupIfProperty; +import io.quarkus.arc.properties.IfBuildProperty; import io.quarkus.redis.datasource.RedisDataSource; import jakarta.enterprise.context.ApplicationScoped; @@ -53,10 +53,15 @@ import stirling.software.common.model.ApplicationProperties.Cluster; // so // the injected RedisDataSource targets the right Valkey. parseUrl/buildClientConfiguration are kept // to validate the URL and to drive that config mapping once it is wired. +// Build-time gating: the whole producer class (RedisDataSource consumer + RedisClient producer) is +// only included in the build when cluster.backplane=valkey. With the default backplane the class +// and +// its producers are removed, so the inactive Redis client has no consumers and no eager startup +// observer is generated. @Slf4j @ApplicationScoped @RequiredArgsConstructor -@LookupIfProperty(name = "cluster.enabled", stringValue = "true") +@IfBuildProperty(name = "cluster.backplane", stringValue = "valkey") public class ValkeyConnectionConfiguration { private final ApplicationProperties applicationProperties; @@ -298,7 +303,6 @@ public class ValkeyConnectionConfiguration { // RedisURI once cluster.valkey credentials handling is finalised. @Produces @Singleton - @LookupIfProperty(name = "cluster.backplane", stringValue = "valkey") public RedisClient nativeRedisClient() { Endpoint endpoint = parseUrl(applicationProperties.getCluster().getValkey().getUrl()); RedisURI.Builder uri = diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyDistributedLock.java b/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyDistributedLock.java index a63e7324e2..dc78a3399f 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyDistributedLock.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyDistributedLock.java @@ -4,7 +4,7 @@ import java.time.Duration; import java.util.Optional; import java.util.UUID; -import io.quarkus.arc.lookup.LookupIfProperty; +import io.quarkus.arc.properties.IfBuildProperty; import io.quarkus.redis.datasource.RedisDataSource; import io.quarkus.redis.datasource.value.SetArgs; import io.quarkus.redis.datasource.value.ValueCommands; @@ -34,9 +34,10 @@ import stirling.software.common.cluster.DistributedLock; // The injected bean is now the RedisDataSource that ValkeyConnectionConfiguration produces; the Lua // scripts and the acquire/release/renew control flow are framework-agnostic and carry over // unchanged. +// Build-time gating: included in the build only when cluster.backplane=valkey; otherwise this bean +// (and its RedisDataSource dependency) is removed so no eager Redis startup observer is generated. @ApplicationScoped -@LookupIfProperty(name = "cluster.enabled", stringValue = "true") -@LookupIfProperty(name = "cluster.backplane", stringValue = "valkey") +@IfBuildProperty(name = "cluster.backplane", stringValue = "valkey") @Slf4j public class ValkeyDistributedLock implements DistributedLock { diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyInstanceRegistry.java b/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyInstanceRegistry.java index 30d0b3c24b..9ad3150b9c 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyInstanceRegistry.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyInstanceRegistry.java @@ -27,10 +27,10 @@ import stirling.software.common.cluster.InstanceRegistry; * Valkey-backed {@link InstanceRegistry}. Each node is stored as a hash with a TTL equal to the * configured heartbeat TTL; the heartbeat re-arms the TTL. */ -// TODO: Migration required - the original @ConditionalOnValkeyBackplane (cluster.enabled=true AND -// cluster.backplane=valkey) was a runtime toggle. Quarkus build-time conditions (@IfBuildProfile / -// @LookupIfProperty) cannot express this composite runtime expression. Guard producer/usage at -// runtime via the Config values, or rework ConditionalOnValkeyBackplane into a CDI lookup guard. +// Build-time gating: included in the build only when cluster.backplane=valkey; otherwise this bean +// (and its RedisDataSource dependency) is removed so no eager Redis startup observer is generated +// and the in-process @DefaultBean InstanceRegistry satisfies the interface. +@io.quarkus.arc.properties.IfBuildProperty(name = "cluster.backplane", stringValue = "valkey") @ApplicationScoped @RequiredArgsConstructor public class ValkeyInstanceRegistry implements InstanceRegistry { diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyJobStore.java b/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyJobStore.java index 99e26a8036..17ceee06df 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyJobStore.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyJobStore.java @@ -44,6 +44,12 @@ import stirling.software.common.cluster.JobStoreEntry; // build-time condition (@io.quarkus.arc.profile.IfBuildProfile / // @io.quarkus.arc.lookup.LookupIfProperty) or guard bean activation at runtime. Annotation left in // place pending that collaborator change. +// Build-time gating: included in the build only when cluster.backplane=valkey; otherwise this bean +// (and its RedisDataSource dependency) is removed so no eager Redis startup observer is generated +// and the in-process @DefaultBean JobStore satisfies the interface. @ConditionalOnValkeyBackplane +// is +// kept for documentation only (it does not propagate guards through Arc). +@io.quarkus.arc.properties.IfBuildProperty(name = "cluster.backplane", stringValue = "valkey") @ApplicationScoped @ConditionalOnValkeyBackplane @Slf4j diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyKeyValueCache.java b/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyKeyValueCache.java index a993ef59fb..eff7a29078 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyKeyValueCache.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyKeyValueCache.java @@ -21,6 +21,10 @@ import stirling.software.common.cluster.KeyValueCache; // @io.quarkus.arc.lookup.LookupIfProperty // or @io.quarkus.arc.profile.IfBuildProfile, or a runtime guard) so Valkey beans only load when // cluster.enabled=true AND cluster.backplane=valkey. +// Build-time gating: included in the build only when cluster.backplane=valkey; otherwise this bean +// (and its RedisDataSource dependency) is removed so no eager Redis startup observer is generated +// and the in-process @DefaultBean KeyValueCache satisfies the interface. +@io.quarkus.arc.properties.IfBuildProperty(name = "cluster.backplane", stringValue = "valkey") @ApplicationScoped public class ValkeyKeyValueCache implements KeyValueCache { diff --git a/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyRateLimitStore.java b/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyRateLimitStore.java index cf9c3df8ac..48d52d2537 100644 --- a/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyRateLimitStore.java +++ b/app/proprietary/src/main/java/stirling/software/proprietary/cluster/valkey/ValkeyRateLimitStore.java @@ -11,7 +11,7 @@ import io.github.bucket4j.distributed.proxy.ProxyManager; import io.github.bucket4j.redis.lettuce.Bucket4jLettuce; import io.lettuce.core.AbstractRedisClient; import io.lettuce.core.RedisClient; -import io.quarkus.arc.lookup.LookupIfProperty; +import io.quarkus.arc.properties.IfBuildProperty; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; @@ -25,13 +25,13 @@ import stirling.software.common.cluster.RateLimitStore; * refills continuously and enforces one global limit across nodes, with the same semantics as the * in-process {@code InProcessRateLimitStore} (which also uses Bucket4j). */ -// @ConditionalOnValkeyBackplane is documentary only under CDI (see that annotation's javadoc); -// the two guards below must be carried directly so the Valkey beans load only when -// cluster.enabled=true AND cluster.backplane=valkey, otherwise the in-process @DefaultBean wins. +// Build-time gating: included in the build only when cluster.backplane=valkey; otherwise this bean +// (and its RedisClient dependency) is removed so no eager Redis startup observer is generated and +// the in-process @DefaultBean RateLimitStore wins. @ConditionalOnValkeyBackplane is documentary +// only. @ApplicationScoped @ConditionalOnValkeyBackplane -@LookupIfProperty(name = "cluster.enabled", stringValue = "true") -@LookupIfProperty(name = "cluster.backplane", stringValue = "valkey") +@IfBuildProperty(name = "cluster.backplane", stringValue = "valkey") public class ValkeyRateLimitStore implements RateLimitStore { private static final String PREFIX = "stirling:rl:";