From ba51140a25d18f5a7bfa75b749c55ad32826cfd5 Mon Sep 17 00:00:00 2001 From: Ryan Emerson Date: Thu, 24 Oct 2024 17:32:22 +0100 Subject: [PATCH] Asynchronously create EmbeddedCacheManager when JDBC_PING2 not required Closes #34313 Signed-off-by: Ryan Emerson --- .../infinispan/CacheManagerFactory.java | 58 ++++++++++++++----- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/infinispan/CacheManagerFactory.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/infinispan/CacheManagerFactory.java index d674a4f063..cef481b8de 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/infinispan/CacheManagerFactory.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/infinispan/CacheManagerFactory.java @@ -28,6 +28,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Stream; @@ -52,6 +53,7 @@ import org.infinispan.configuration.global.ShutdownHookBehavior; import org.infinispan.configuration.parsing.ConfigurationBuilderHolder; import org.infinispan.configuration.parsing.ParserRegistry; import org.infinispan.manager.DefaultCacheManager; +import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.metrics.config.MicrometerMeterRegisterConfigurationBuilder; import org.infinispan.persistence.remote.configuration.ExhaustedAction; import org.infinispan.persistence.remote.configuration.RemoteStoreConfigurationBuilder; @@ -110,12 +112,21 @@ public class CacheManagerFactory { private static final Logger logger = Logger.getLogger(CacheManagerFactory.class); + private final CompletableFuture cacheManagerFuture; private final CompletableFuture remoteCacheManagerFuture; - private final String config; - private volatile DefaultCacheManager cacheManager; + private final Function jdbcCacheManagerFunction; + private volatile EmbeddedCacheManager cacheManager; public CacheManagerFactory(String config) { - this.config = config; + ConfigurationBuilderHolder builder = new ParserRegistry().parse(config); + if (!isJdbcPingRequired(builder)) { + cacheManagerFuture = CompletableFuture.supplyAsync(() -> startEmbeddedCacheManager(builder, null)); + jdbcCacheManagerFunction = null; + } else { + cacheManagerFuture = null; + jdbcCacheManagerFunction = em -> startEmbeddedCacheManager(builder, em); + } + if (InfinispanUtils.isRemoteInfinispan()) { logger.debug("Remote Cache feature is enabled"); this.remoteCacheManagerFuture = CompletableFuture.supplyAsync(this::startRemoteCacheManager); @@ -125,11 +136,32 @@ public class CacheManagerFactory { } } - public DefaultCacheManager getOrCreateEmbeddedCacheManager(KeycloakSession keycloakSession) { + private static boolean isJdbcPingRequired(ConfigurationBuilderHolder builder) { + if (InfinispanUtils.isRemoteInfinispan()) + return false; + + var transportConfig = builder.getGlobalConfigurationBuilder().transport(); + if (transportConfig.getTransport() == null) + return false; + + String transportStack = Configuration.getRawValue("kc.cache-stack"); + if (transportStack != null && !isJdbcPingStack(transportStack)) + return false; + + var stackXmlAttribute = transportConfig.defaultTransport().attributes().attribute(STACK); + return !stackXmlAttribute.isModified() || isJdbcPingStack(stackXmlAttribute.get()); + } + + public EmbeddedCacheManager getOrCreateEmbeddedCacheManager(KeycloakSession keycloakSession) { + if (cacheManagerFuture != null) + return join(cacheManagerFuture); + if (cacheManager == null) { synchronized (this) { - if (cacheManager == null) - cacheManager = startEmbeddedCacheManager(keycloakSession); + if (cacheManager == null) { + EntityManager em = keycloakSession.getProvider(JpaConnectionProvider.class).getEntityManager(); + cacheManager = jdbcCacheManagerFunction.apply(em); + } } } return cacheManager; @@ -286,9 +318,8 @@ public class CacheManagerFactory { admin.reindexCache(cacheName); } - private DefaultCacheManager startEmbeddedCacheManager(KeycloakSession keycloakSession) { + private EmbeddedCacheManager startEmbeddedCacheManager(ConfigurationBuilderHolder builder, EntityManager em) { logger.info("Starting Infinispan embedded cache manager"); - ConfigurationBuilderHolder builder = new ParserRegistry().parse(config); // We must disable the Infinispan default ShutdownHook as we manage the EmbeddedCacheManager lifecycle explicitly // with #shutdown and multiple calls to EmbeddedCacheManager#stop can lead to Exceptions being thrown @@ -322,7 +353,7 @@ public class CacheManagerFactory { } else { // embedded mode! if (builder.getNamedConfigurationBuilders().entrySet().stream().anyMatch(c -> c.getValue().clustering().cacheMode().isClustered())) { - configureTransportStack(builder, keycloakSession); + configureTransportStack(builder, em); configureRemoteStores(builder); } configureCacheMaxCount(builder, CachingOptions.CLUSTERED_MAX_COUNT_CACHES); @@ -368,7 +399,7 @@ public class CacheManagerFactory { return Integer.getInteger("kc.cache-ispn-start-timeout", 120); } - private void configureTransportStack(ConfigurationBuilderHolder builder, KeycloakSession keycloakSession) { + private static void configureTransportStack(ConfigurationBuilderHolder builder, EntityManager em) { var transportConfig = builder.getGlobalConfigurationBuilder().transport(); if (Configuration.isTrue(CachingOptions.CACHE_EMBEDDED_MTLS_ENABLED_PROPERTY)) { validateTlsAvailable(transportConfig.build()); @@ -405,7 +436,6 @@ public class CacheManagerFactory { stackXmlAttribute.isModified() ? stackXmlAttribute.get() : "jdbc-ping-udp"; var udp = stackName.endsWith("udp"); - EntityManager em = keycloakSession.getProvider(JpaConnectionProvider.class).getEntityManager(); var tableName = JpaUtils.getTableNameForNativeQuery("JGROUPS_PING", em); var attributes = Map.of( // Leave initialize_sql blank as table is already created by Keycloak @@ -429,16 +459,16 @@ public class CacheManagerFactory { transportConfig.defaultTransport().stack(stackName); } - private void warnDeprecatedCloudStack(String stackName) { + private static void warnDeprecatedCloudStack(String stackName) { switch (stackName) { case "azure": case "ec2": case "google": - logger.warnf("Stack '%s' is deprecated. We recommend to use 'jdbc-ping' instead", stackName); + Logger.getLogger(CacheManagerFactory.class).warnf("Stack '%s' is deprecated. We recommend to use 'jdbc-ping' instead", stackName); } } - private boolean isJdbcPingStack(String stackName) { + private static boolean isJdbcPingStack(String stackName) { return "jdbc-ping".equals(stackName) || "jdbc-ping-udp".equals(stackName); }