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:
parent
565bc7d664
commit
26e2fde115
5 changed files with 48 additions and 33 deletions
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,62 +141,67 @@ 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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in a new issue