8000 KEYCLOAK-19783 Cached user's session ids for faster user sessions retrieval by gideonray · Pull Request #8715 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@
<param name="lifespan" value="900000000000000000"/>
</feature>
</feature>
<feature spec="subsystem.infinispan.cache-container.distributed-cache">
<param name="distributed-cache" value="sessionIds"/>
<param name="owners" value="1"/>
<feature spec="subsystem.infinispan.cache-container.distributed-cache.component.expiration">
<param name="lifespan" value="900000000000000000"/>
</feature>
</feature>
<feature spec="subsystem.infinispan.cache-container.distributed-cache">
<param name="distributed-cache" value="authenticationSessions"/>
<param name="owners" value="1"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
<feature spec="subsystem.infinispan.cache-container.local-cache">
<param name="local-cache" value="sessions"/>
</feature>
<feature spec="subsystem.infinispan.cache-container.local-cache">
<param name="local-cache" value="sessionIds"/>
</feature>
<feature spec="subsystem.infinispan.cache-container.local-cache">
<param name="local-cache" value="authenticationSessions"/>
</feature>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ embed-server --server-config=standalone.xml
/subsystem=infinispan/cache-container=keycloak/local-cache=users:add()
/subsystem=infinispan/cache-container=keycloak/local-cache=users/memory=object:add(size=10000)
/subsystem=infinispan/cache-container=keycloak/local-cache=sessions:add()
/subsystem=infinispan/cache-container=keycloak/local-cache=sessionIds:add()
/subsystem=infinispan/cache-container=keycloak/local-cache=authenticationSessions:add()
/subsystem=infinispan/cache-container=keycloak/local-cache=offlineSessions:add()
/subsystem=infinispan/cache-container=keycloak/local-cache=clientSessions:add()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ embed-server --server-config=standalone-ha.xml
/subsystem=infinispan/cache-container=keycloak/local-cache=users:add()
/subsystem=infinispan/cache-container=keycloak/local-cache=users/memory=object:add(size=10000)
/subsystem=infinispan/cache-container=keycloak/distributed-cache=sessions:add(owners="1")
/subsystem=infinispan/cache-container=keycloak/distributed-cache=sessionIds:add(owners="1")
/subsystem=infinispan/cache-container=keycloak/distributed-cache=authenticationSessions:add(owners="1")
/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineSessions:add(owners="1")
/subsystem=infinispan/cache-container=keycloak/distributed-cache=clientSessions:add(owners="1")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,14 @@ protected void initEmbedded() {
Configuration sessionCacheConfiguration = sessionConfigBuilder.build();
cacheManager.defineConfiguration(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, sessionCacheConfiguration);

if (jdgEnabled) {
sessionConfigBuilder = createCacheConfigurationBuilder();
sessionConfigBuilder.read(sessionCacheConfigurationBase);
configureRemoteCacheStore(sessionConfigBuilder, async, InfinispanConnectionProvider.USER_SESSION_IDS_CACHE_NAME);
}
sessionCacheConfiguration = sessionConfigBuilder.build();
cacheManager.defineConfiguration(InfinispanConnectionProvider.USER_SESSION_IDS_CACHE_NAME, sessionCacheConfiguration);

if (jdgEnabled) {
sessionConfigBuilder = createCacheConfigurationBuilder();
sessionConfigBuilder.read(sessionCacheConfigurationBase);
Expand Down Expand Up @@ -299,6 +307,7 @@ protected void initEmbedded() {

// Retrieve caches to enforce rebalance
cac 10000 heManager.getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME, true);
cacheManager.getCache(InfinispanConnectionProvider.USER_SESSION_IDS_CACHE_NAME, true);
cacheManager.getCache(InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME, true);
cacheManager.getCache(InfinispanConnectionProvider.CLIENT_SESSION_CACHE_NAME, true);
cacheManager.getCache(InfinispanConnectionProvider.OFFLINE_CLIENT_SESSION_CACHE_NAME, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public interface InfinispanConnectionProvider extends Provider {
int USER_REVISIONS_CACHE_DEFAULT_MAX = 100000;

String USER_SESSION_CACHE_NAME = "sessions";
String USER_SESSION_IDS_CACHE_NAME = "sessionIds";
String CLIENT_SESSION_CACHE_NAME = "clientSessions";
String OFFLINE_USER_SESSION_CACHE_NAME = "offlineSessions";
String OFFLINE_CLIENT_SESSION_CACHE_NAME = "offlineClientSessions";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.keycloak.models.sessions.infinispan;

import org.infinispan.Cache;
import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
import org.jboss.logging.Logger;
import org.keycloak.models.sessions.infinispan.changes.SessionEntityWrapper;
import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CompletionStage;

@Listener
public class InfinispanUserSessionListener {

public static final Logger logger = Logger.getLogger(InfinispanUserSessionListener.class);

private final Cache<String, Set<String>> sessionIdsCache;

public InfinispanUserSessionListener(Cache<String, Set<String>> sessionIdsCache) {
this.sessionIdsCache = sessionIdsCache;
}

@CacheEntryCreated
public void listen(CacheEntryCreatedEvent<String, SessionEntityWrapper<UserSessionEntity>> event) {
if (event.isPre()) {
return;
}

final String userId = event.getValue().getEntity().getUser();
final Set<String> sessionIds = sessionIdsCache.getOrDefault(userId, new HashSet<>());
sessionIds.add(event.getKey());
sessionIdsCache.put(userId, sessionIds);
}

@CacheEntryRemoved
public CompletionStage<Void> listen(CacheEntryRemovedEvent<String, SessionEntityWrapper<UserSessionEntity>> event) {
if (!event.isPre()) {
return null;
}

final String userId = event.getValue().getEntity().getUser();
final Set<String> sessionIds = sessionIdsCache.get(userId);

if (sessionIds == null) {
return null;
}

final Set<String> newSessionIds = new HashSet<>(sessionIds);
newSessionIds.remove(event.getKey());

if (newSessionIds.isEmpty()) {
if (!sessionIdsCache.remove(userId, sessionIds)) {
logger.warnf("Failed to remove session ids for user '%s' in cache '%s'", userId, sessionIdsCache.getName());
}
} else if (!sessionIdsCache.replace(userId, sessionIds, newSessionIds)) {
logger.warnf("Failed to replace session ids for user '%s' in cache '%s'", userId, sessionIdsCache.getName());
}

return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
protected final KeycloakSession session;

protected final Cache<String, SessionEntityWrapper<UserSessionEntity>> sessionCache;
protected final Cache<String, Set<String>> sessionIdsCache;
protected final Cache<String, SessionEntityWrapper<UserSessionEntity>> offlineSessionCache;
protected final Cache<UUID, SessionEntityWrapper<AuthenticatedClientSessionEntity>> clientSessionCache;
protected final Cache<UUID, SessionEntityWrapper<AuthenticatedClientSessionEntity>> offlineClientSessionCache;
Expand Down Expand Up @@ -117,13 +118,15 @@ public InfinispanUserSessionProvider(KeycloakSession session,
PersisterLastSessionRefreshStore persisterLastSessionRefreshStore,
InfinispanKeyGenerator keyGenerator,
Cache<String, SessionEntityWrapper<UserSessionEntity>> sessionCache,
Cache<String, Set<String>> sessionIdsCache,
Cache<String, SessionEntityWrapper<UserSessionEntity>> offlineSessionCache,
Cache<UUID, SessionEntityWrapper<AuthenticatedClientSessionEntity>> clientSessionCache,
Cache<UUID, SessionEntityWrapper<AuthenticatedClientSessionEntity>> offlineClientSessionCache,
boolean loadOfflineSessionsStatsFromDatabase) {
this.session = session;

this.sessionCache = sessionCache;
this.sessionIdsCache = sessionIdsCache;
this.clientSessionCache = clientSessionCache;
this.offlineSessionCache = offlineSessionCache;
this.offlineClientSessionCache = offlineClientSessionCache;
Expand Down Expand Up @@ -365,8 +368,16 @@ protected Stream<UserSessionModel> getUserSessionsStream(RealmModel realm, UserS

}

Cache<String, SessionEntityWrapper<UserSessionEntity>> cache = getCache(offline);
cache = CacheDecorators.skipCacheLoaders(cache);
final Cache<String, SessionEntityWrapper<UserSessionEntity>> cache = CacheDecorators.skipCacheLoaders(getCache(offline));

if (predicate.getUserId() != null && !offline) {
// return a stream via session ids cache for faster user session retrieval
final Set<String> sessionIds = sessionIdsCache.getOrDefault(predicate.getUserId(), Collections.emptySet());
return sessionIds.stream().map(id -> cache.get(id)).filter(Objects::nonNull)
.map(wrapper -> Map.entry(wrapper.getEntity().getId(), wrapper)).filter(predicate)
.map(Mappers.userSessionEntity())
.map(entity -> this.wrap(realm, entity, false));
}

// return a stream that 'wraps' the infinispan cache stream so that the cache stream's elements are read one by one
// and then mapped locally to avoid serialization issues when trying to manipulate the cache stream directly.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,19 +86,31 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
private CrossDCLastSessionRefreshStore offlineLastSessionRefreshStore;
private PersisterLastSessionRefreshStore persisterLastSessionRefreshStore;
private InfinispanKeyGenerator keyGenerator;
private InfinispanUserSessionListener userSessionListener;

@Override
public InfinispanUserSessionProvider create(KeycloakSession session) {
InfinispanConnectionProvider connections = session.getProvider(InfinispanConnectionProvider.class);
Cache<String, SessionEntityWrapper<UserSessionEntity>> cache = connections.getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME);
Cache<String, Set<String>> sessionIdsCache = connections.getCache(InfinispanConnectionProvider.USER_SESSION_IDS_CACHE_NAME);
Cache<String, SessionEntityWrapper<UserSessionEntity>> offlineSessionsCache = connections.getCache(InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME);
Cache<UUID, SessionEntityWrapper<AuthenticatedClientSessionEntity>> clientSessionCache = connections.getCache(InfinispanConnectionProvider.CLIENT_SESSION_CACHE_NAME);
Cache<UUID, SessionEntityWrapper<AuthenticatedClientSessionEntity>> offlineClientSessionsCache = connections.getCache(InfinispanConnectionProvider.OFFLINE_CLIENT_SESSION_CACHE_NAME);

boolean loadOfflineSessionsStatsFromDatabase = !isPreloadingOfflineSessionsFromDatabaseEnabled();

if (userSessionListener == null) {
synchronized (this) {
if (userSessionListener == null) {
userSessionListener = new InfinispanUserSessionListener(sessionIdsCache);
cache.addListener(userSessionListener);
}
}
}

return new InfinispanUserSessionProvider(session, remoteCacheInvoker, lastSessionRefreshStore, offlineLastSessionRefreshStore,
persisterLastSessionRefreshStore, keyGenerator, cache, offlineSessionsCache, clientSessionCache, offlineClientSessionsCache, loadOfflineSessionsStatsFromDatabase);
persisterLastSessionRefreshStore, keyGenerator, cache, sessionIdsCache, offlineSessionsCache, clientSessionCache,
offlineClientSessionsCache, loadOfflineSessionsStatsFromDatabase);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,23 @@ echo ** Update distributed-cache sessions element **
)
/subsystem=infinispan/cache-container=keycloak/distributed-cache=sessions:write-attribute(name=statistics-enabled,value=true)

echo ** Update distributed-cache session ids element **
/subsystem=infinispan/cache-container=keycloak/distributed-cache=sessionIds/store=remote:add( \
passivation=false, \
fetch-state=false, \
purge=false, \
preload=false, \
shared=true, \
remote-servers=["remote-cache"], \
cache=sessionIds, \
properties={ \
rawValues=true, \
marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory, \
protocolVersion=${keycloak.connectionsInfinispan.hotrodProtocolVersion} \
} \
)
/subsystem=infinispan/cache-container=keycloak/distributed-cache=sessionIds:write-attribute(name=statistics-enabled,value=true)

echo ** Update distributed-cache offlineSessions element **
/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineSessions/store=remote:add( \
passivation=false, \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ cd replicated-cache-configuration=sessions-cfg
cd /subsystem=datagrid-infinispan/cache-container=clustered
./replicated-cache=work:add(configuration=sessions-cfg)
./replicated-cache=sessions:add(configuration=sessions-cfg)
./replicated-cache=sessionsIds:add(configuration=sessions-cfg)
./replicated-cache=clientSessions:add(configuration=sessions-cfg)
./replicated-cache=offlineSessions:add(configuration=sessions-cfg)
./replicated-cache=offlineClientSessions:add(configuration=sessions-cfg)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ cd /subsystem=infinispan/cache-container=keycloak

./replicated-cache=work/store=remote:add(cache=work, fetch-state=false, passivation=false, preload=false, purge=false, remote-servers=["remote-cache"], shared=true, properties={rawValues=true, marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory, protocolVersion=${env.HOTROD_VERSION:2.8}})
./distributed-cache=sessions/store=remote:add(cache=sessions, fetch-state=false, passivation=false, preload=false, purge=false, remote-servers=["remote-cache"], shared=true, properties={rawValues=true, marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory, protocolVersion=${env.HOTROD_VERSION:2.8}})
./distributed-cache=sessionIds/store=remote:add(cache=sessionIds fetch-state=false, passivation=false, preload=false, purge=false, remote-servers=["remote-cache"], shared=true, properties={rawValues=true, marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory, protocolVersion=${env.HOTROD_VERSION:2.8}})
./distributed-cache=offlineSessions/store=remote:add(cache=offlineSessions, fetch-state=false, passivation=false, preload=false, purge=false, remote-servers=["remote-cache"], shared=true, properties={rawValues=true, marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory, protocolVersion=${env.HOTROD_VERSION:2.8}})
./distributed-cache=clientSessions/store=remote:add(cache=clientSessions, fetch-state=false, passivation=false, preload=false, purge=false, remote-servers=["remote-cache"], shared=true, properties={rawValues=true, marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory, protocolVersion=${env.HOTROD_VERSION:2.8}})
./distributed-cache=offlineClientSessions/store=remote:add(cache=offlineClientSessions, fetch-state=false, passivation=false, preload=false, purge=false, remote-servers=["remote-cache"], shared=true, properties={rawValues=true, marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory, protocolVersion=${env.HOTROD_VERSION:2.8}})
Expand All @@ -18,6 +19,7 @@ cd /subsystem=infinispan/cache-container=keycloak

./replicated-cache=work:write-attribute (name=statistics-enabled, value=${env.CACHE_STATISTICS:true})
./distributed-cache=sessions:write-attribute (name=statistics-enabled, value=${env.CACHE_STATISTICS:true})
./distributed-cache=sessionIds:write-attribute (name=statistics-enabled, value=${env.CACHE_STATISTICS:true})
./distributed-cache=offlineSessions:write-attribute (name=statistics-enabled, value=${env.CACHE_STATISTICS:true})
./distributed-cache=clientSessions:write-attribute (name=statistics-enabled, value=${env.CACHE_STATISTICS:true})
./distributed-cache=offlineClientSessions:write-attribute (name=statistics-enabled, value=${env.CACHE_STATISTICS:true})
Expand Down
0