Avoid reseting cachemanger to null to avoid a re-initialization (#24086)

Also follow best practices of using volatile variables for double-locking, and not using shutdown caches.

Closes #24085
This commit is contained in:
Alexander Schwartz 2023-11-08 17:33:44 +01:00 committed by GitHub
parent 565bc7d664
commit 26e2fde115
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 48 additions and 33 deletions

View file

@ -19,6 +19,7 @@ package org.keycloak.cluster.infinispan;
import org.infinispan.Cache;
import org.infinispan.client.hotrod.exceptions.HotRodClientException;
import org.infinispan.lifecycle.ComponentStatus;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachemanagerlistener.annotation.ViewChanged;
@ -230,7 +231,11 @@ public class InfinispanClusterProviderFactory implements ClusterProviderFactory,
Tracked in https://github.com/keycloak/keycloak/issues/9871
*/
synchronized (DefaultInfinispanConnectionProviderFactory.class) {
workCache.entrySet().removeIf(new LockEntryPredicate(removedNodesAddresses));
if (workCache.getStatus() == ComponentStatus.RUNNING) {
workCache.entrySet().removeIf(new LockEntryPredicate(removedNodesAddresses));
} else {
logger.warn("work cache is not running, ignoring event");
}
}
}
} catch (Throwable t) {

View file

@ -66,17 +66,17 @@ import static org.keycloak.models.cache.infinispan.InfinispanCacheRealmProviderF
*/
public class DefaultInfinispanConnectionProviderFactory implements InfinispanConnectionProviderFactory, EnvironmentDependentProviderFactory {
protected static final Logger logger = Logger.getLogger(DefaultInfinispanConnectionProviderFactory.class);
private static final Logger logger = Logger.getLogger(DefaultInfinispanConnectionProviderFactory.class);
protected Config.Scope config;
private Config.Scope config;
protected EmbeddedCacheManager cacheManager;
private volatile EmbeddedCacheManager cacheManager;
protected RemoteCacheProvider remoteCacheProvider;
private volatile RemoteCacheProvider remoteCacheProvider;
protected boolean containerManaged;
protected volatile boolean containerManaged;
private TopologyInfo topologyInfo;
private volatile TopologyInfo topologyInfo;
@Override
public InfinispanConnectionProvider create(KeycloakSession session) {
@ -102,7 +102,6 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon
if (remoteCacheProvider != null) {
remoteCacheProvider.stop();
}
cacheManager = null;
}
}
@ -143,61 +142,66 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon
managedCacheManager = provider.getCacheManager(config);
}
// store it in a locale variable first, so it is not visible to the outside, yet
EmbeddedCacheManager localCacheManager;
if (managedCacheManager == null) {
if (!config.getBoolean("embedded", false)) {
throw new RuntimeException("No " + ManagedCacheManagerProvider.class.getName() + " found. If running in embedded mode set the [embedded] property to this provider.");
}
initEmbedded();
localCacheManager = initEmbedded();
} else {
initContainerManaged(managedCacheManager);
localCacheManager = initContainerManaged(managedCacheManager);
}
logger.infof(topologyInfo.toString());
remoteCacheProvider = new RemoteCacheProvider(config, cacheManager);
remoteCacheProvider = new RemoteCacheProvider(config, localCacheManager);
// only set the cache manager attribute at the very end to avoid passing a half-initialized entry callers
cacheManager = localCacheManager;
}
}
}
}
protected void initContainerManaged(EmbeddedCacheManager cacheManager) {
this.cacheManager = cacheManager;
protected EmbeddedCacheManager initContainerManaged(EmbeddedCacheManager cacheManager) {
containerManaged = true;
long realmRevisionsMaxEntries = this.cacheManager.getCache(InfinispanConnectionProvider.REALM_CACHE_NAME).getCacheConfiguration().memory().size();
long realmRevisionsMaxEntries = cacheManager.getCache(InfinispanConnectionProvider.REALM_CACHE_NAME).getCacheConfiguration().memory().size();
realmRevisionsMaxEntries = realmRevisionsMaxEntries > 0
? 2 * realmRevisionsMaxEntries
: InfinispanConnectionProvider.REALM_REVISIONS_CACHE_DEFAULT_MAX;
this.cacheManager.defineConfiguration(InfinispanConnectionProvider.REALM_REVISIONS_CACHE_NAME, getRevisionCacheConfig(realmRevisionsMaxEntries));
this.cacheManager.getCache(InfinispanConnectionProvider.REALM_REVISIONS_CACHE_NAME, true);
cacheManager.defineConfiguration(InfinispanConnectionProvider.REALM_REVISIONS_CACHE_NAME, getRevisionCacheConfig(realmRevisionsMaxEntries));
cacheManager.getCache(InfinispanConnectionProvider.REALM_REVISIONS_CACHE_NAME, true);
long userRevisionsMaxEntries = this.cacheManager.getCache(InfinispanConnectionProvider.USER_CACHE_NAME).getCacheConfiguration().memory().size();
long userRevisionsMaxEntries = cacheManager.getCache(InfinispanConnectionProvider.USER_CACHE_NAME).getCacheConfiguration().memory().size();
userRevisionsMaxEntries = userRevisionsMaxEntries > 0
? 2 * userRevisionsMaxEntries
: InfinispanConnectionProvider.USER_REVISIONS_CACHE_DEFAULT_MAX;
this.cacheManager.defineConfiguration(InfinispanConnectionProvider.USER_REVISIONS_CACHE_NAME, getRevisionCacheConfig(userRevisionsMaxEntries));
this.cacheManager.getCache(InfinispanConnectionProvider.USER_REVISIONS_CACHE_NAME, true);
this.cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME, true);
this.cacheManager.getCache(InfinispanConnectionProvider.AUTHENTICATION_SESSIONS_CACHE_NAME, true);
this.cacheManager.getCache(InfinispanConnectionProvider.KEYS_CACHE_NAME, true);
this.cacheManager.getCache(InfinispanConnectionProvider.ACTION_TOKEN_CACHE, true);
cacheManager.defineConfiguration(InfinispanConnectionProvider.USER_REVISIONS_CACHE_NAME, getRevisionCacheConfig(userRevisionsMaxEntries));
cacheManager.getCache(InfinispanConnectionProvider.USER_REVISIONS_CACHE_NAME, true);
cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME, true);
cacheManager.getCache(InfinispanConnectionProvider.AUTHENTICATION_SESSIONS_CACHE_NAME, true);
cacheManager.getCache(InfinispanConnectionProvider.KEYS_CACHE_NAME, true);
cacheManager.getCache(InfinispanConnectionProvider.ACTION_TOKEN_CACHE, true);
long authzRevisionsMaxEntries = this.cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME).getCacheConfiguration().memory().size();
long authzRevisionsMaxEntries = cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME).getCacheConfiguration().memory().size();
authzRevisionsMaxEntries = authzRevisionsMaxEntries > 0
? 2 * authzRevisionsMaxEntries
: InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_DEFAULT_MAX;
this.cacheManager.defineConfiguration(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME, getRevisionCacheConfig(authzRevisionsMaxEntries));
this.cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME, true);
cacheManager.defineConfiguration(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME, getRevisionCacheConfig(authzRevisionsMaxEntries));
cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME, true);
this.topologyInfo = new TopologyInfo(this.cacheManager, config, false);
this.topologyInfo = new TopologyInfo(cacheManager, config, false);
logger.debugv("Using container managed Infinispan cache container, lookup={0}", cacheManager);
return cacheManager;
}
protected void initEmbedded() {
protected EmbeddedCacheManager initEmbedded() {
GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder();
boolean clustered = config.getBoolean("clustered", false);
@ -221,7 +225,7 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon
// See https://infinispan.org/docs/stable/titles/developing/developing.html#marshalling for the details
gcb.serialization().marshaller(new JBossUserMarshaller());
cacheManager = new DefaultCacheManager(gcb.build());
EmbeddedCacheManager cacheManager = new DefaultCacheManager(gcb.build());
if (useKeycloakTimeService) {
setTimeServiceToKeycloakTime(cacheManager);
}
@ -374,6 +378,8 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon
cacheManager.defineConfiguration(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME, getRevisionCacheConfig(authzRevisionsMaxEntries));
cacheManager.getCache(InfinispanConnectionProvider.AUTHORIZATION_REVISIONS_CACHE_NAME, true);
return cacheManager;
}
private Configuration getRevisionCacheConfig(long maxEntries) {

View file

@ -29,11 +29,12 @@ public class LegacyInfinispanConnectionFactory extends DefaultInfinispanConnecti
implements EnvironmentDependentProviderFactory {
@Override
protected void initContainerManaged(EmbeddedCacheManager cacheManager) {
super.initContainerManaged(cacheManager);
protected EmbeddedCacheManager initContainerManaged(EmbeddedCacheManager cacheManager) {
EmbeddedCacheManager result = super.initContainerManaged(cacheManager);
// force closing the cache manager when stopping the provider
// we probably want to refactor the default impl a bit to support this use case
containerManaged = false;
return result;
}
@Override

View file

@ -148,6 +148,8 @@
<!-- See also https://github.com/wildfly/wildfly-core/blob/7e5624cf92ebe4b64a4793a8c0b2a340c0d6d363/core-feature-pack/common/src/main/resources/content/bin/common.sh#L57-L60 -->
<argLine>@{argLine} -Xmx1536m -XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED</argLine>
<systemPropertyVariables>
<!-- ensure a log entry if we're running out of JGroup threads -->
<jgroups.thread_dumps_threshold>1</jgroups.thread_dumps_threshold>
<!-- keycloak.model.parameters lists parameter classes from
org.keycloak.model.parameters package and determine enabled providers -->
<keycloak.model.parameters>${keycloak.model.parameters}</keycloak.model.parameters>

View file

@ -24,6 +24,7 @@ import org.keycloak.common.util.SystemEnvProperties;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BooleanSupplier;
import java.util.stream.Collectors;
@ -35,7 +36,7 @@ public class Config implements ConfigProvider {
private final Properties systemProperties = new SystemEnvProperties();
private final Map<String, String> defaultProperties = new HashMap<>();
private final Map<String, String> defaultProperties = new ConcurrentHashMap<>();
private final ThreadLocal<Map<String, String>> properties = new ThreadLocal<Map<String, String>>() {
@Override
protected Map<String, String> initialValue() {