From d9dfe74e8b448ba743b66d2bb7dc5eff787a577c Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Mon, 9 Sep 2024 10:47:56 +0200 Subject: [PATCH] Set idle time the same as for the internal cache, but extend it for refreshes Closes #32100 Signed-off-by: Alexander Schwartz Signed-off-by: Michal Hajas Co-authored-by: Michal Hajas --- ...nispanUserLoginFailureProviderFactory.java | 7 ++- .../InfinispanUserSessionProviderFactory.java | 22 +++------ .../remotestore/RemoteCacheInvoker.java | 49 ++++++------------- 3 files changed, 24 insertions(+), 54 deletions(-) diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserLoginFailureProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserLoginFailureProviderFactory.java index 2e5ad884ad..54475b68d2 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserLoginFailureProviderFactory.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserLoginFailureProviderFactory.java @@ -135,11 +135,10 @@ public class InfinispanUserLoginFailureProviderFactory implements UserLoginFailu InfinispanConnectionProvider ispn = session.getProvider(InfinispanConnectionProvider.class); Cache> loginFailuresCache = ispn.getCache(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME); - checkRemoteCache(session, loginFailuresCache, (RealmModel realm) -> - Time.toMillis(realm.getMaxDeltaTimeSeconds()), SessionTimeouts::getLoginFailuresLifespanMs, SessionTimeouts::getLoginFailuresMaxIdleMs); + checkRemoteCache(session, loginFailuresCache, SessionTimeouts::getLoginFailuresLifespanMs, SessionTimeouts::getLoginFailuresMaxIdleMs); } - private RemoteCache checkRemoteCache(KeycloakSession session, Cache> ispnCache, RemoteCacheInvoker.MaxIdleTimeLoader maxIdleLoader, + private RemoteCache checkRemoteCache(KeycloakSession session, Cache> ispnCache, SessionFunction lifespanMsLoader, SessionFunction maxIdleTimeMsLoader) { Set remoteStores = InfinispanUtil.getRemoteStores(ispnCache); @@ -155,7 +154,7 @@ public class InfinispanUserLoginFailureProviderFactory implements UserLoginFailu throw new IllegalStateException("No remote cache available for the infinispan cache: " + ispnCache.getName()); } - remoteCacheInvoker.addRemoteCache(ispnCache.getName(), remoteCache, maxIdleLoader); + remoteCacheInvoker.addRemoteCache(ispnCache.getName(), remoteCache); RemoteCacheSessionListener hotrodListener = RemoteCacheSessionListener.createListener(session, ispnCache, remoteCache, lifespanMsLoader, maxIdleTimeMsLoader, null); remoteCache.addClientListener(hotrodListener); diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java index 2bf564e462..7d47d2281e 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProviderFactory.java @@ -317,37 +317,27 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider InfinispanConnectionProvider ispn = session.getProvider(InfinispanConnectionProvider.class); Cache> sessionsCache = ispn.getCache(InfinispanConnectionProvider.USER_SESSION_CACHE_NAME); - RemoteCache sessionsRemoteCache = checkRemoteCache(session, sessionsCache, (RealmModel realm) -> { - // We won't write to the remoteCache during token refresh, so the timeout needs to be longer. - return Time.toMillis(realm.getSsoSessionMaxLifespan()); - }, SessionTimeouts::getUserSessionLifespanMs, SessionTimeouts::getUserSessionMaxIdleMs); + RemoteCache sessionsRemoteCache = checkRemoteCache(session, sessionsCache, SessionTimeouts::getUserSessionLifespanMs, SessionTimeouts::getUserSessionMaxIdleMs); if (sessionsRemoteCache != null) { lastSessionRefreshStore = new CrossDCLastSessionRefreshStoreFactory().createAndInit(session, sessionsCache, false); } Cache> clientSessionsCache = ispn.getCache(InfinispanConnectionProvider.CLIENT_SESSION_CACHE_NAME); - checkRemoteCache(session, clientSessionsCache, (RealmModel realm) -> { - // We won't write to the remoteCache during token refresh, so the timeout needs to be longer. - return Time.toMillis(realm.getSsoSessionMaxLifespan()); - }, SessionTimeouts::getClientSessionLifespanMs, SessionTimeouts::getClientSessionMaxIdleMs); + checkRemoteCache(session, clientSessionsCache, SessionTimeouts::getClientSessionLifespanMs, SessionTimeouts::getClientSessionMaxIdleMs); Cache> offlineSessionsCache = ispn.getCache(InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME); - RemoteCache offlineSessionsRemoteCache = checkRemoteCache(session, offlineSessionsCache, (RealmModel realm) -> { - return Time.toMillis(realm.getOfflineSessionIdleTimeout()); - }, this::deriveOfflineSessionCacheEntryLifespanMs, SessionTimeouts::getOfflineSessionMaxIdleMs); + RemoteCache offlineSessionsRemoteCache = checkRemoteCache(session, offlineSessionsCache, this::deriveOfflineSessionCacheEntryLifespanMs, SessionTimeouts::getOfflineSessionMaxIdleMs); if (offlineSessionsRemoteCache != null) { offlineLastSessionRefreshStore = new CrossDCLastSessionRefreshStoreFactory().createAndInit(session, offlineSessionsCache, true); } Cache> offlineClientSessionsCache = ispn.getCache(InfinispanConnectionProvider.OFFLINE_CLIENT_SESSION_CACHE_NAME); - checkRemoteCache(session, offlineClientSessionsCache, (RealmModel realm) -> { - return Time.toMillis(realm.getOfflineSessionIdleTimeout()); - }, this::deriveOfflineClientSessionCacheEntryLifespanOverrideMs, SessionTimeouts::getOfflineClientSessionMaxIdleMs); + checkRemoteCache(session, offlineClientSessionsCache, this::deriveOfflineClientSessionCacheEntryLifespanOverrideMs, SessionTimeouts::getOfflineClientSessionMaxIdleMs); } - private RemoteCache checkRemoteCache(KeycloakSession session, Cache> ispnCache, RemoteCacheInvoker.MaxIdleTimeLoader maxIdleLoader, + private RemoteCache checkRemoteCache(KeycloakSession session, Cache> ispnCache, SessionFunction lifespanMsLoader, SessionFunction maxIdleTimeMsLoader) { Set remoteStores = InfinispanUtil.getRemoteStores(ispnCache); @@ -363,7 +353,7 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider throw new IllegalStateException("No remote cache available for the infinispan cache: " + ispnCache.getName()); } - remoteCacheInvoker.addRemoteCache(ispnCache.getName(), remoteCache, maxIdleLoader); + remoteCacheInvoker.addRemoteCache(ispnCache.getName(), remoteCache); Runnable onFailover = null; if (useCaches && MultiSiteUtils.isPersistentSessionsEnabled()) { diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheInvoker.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheInvoker.java index ad3a70f42d..8d5f6c654d 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheInvoker.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/remotestore/RemoteCacheInvoker.java @@ -51,12 +51,11 @@ public class RemoteCacheInvoker { public static final Logger logger = Logger.getLogger(RemoteCacheInvoker.class); - private final Map remoteCaches = new HashMap<>(); + private final Map remoteCaches = new HashMap<>(); - public void addRemoteCache(String cacheName, RemoteCache remoteCache, MaxIdleTimeLoader maxIdleLoader) { - RemoteCacheContext ctx = new RemoteCacheContext(remoteCache, maxIdleLoader); - remoteCaches.put(cacheName, ctx); + public void addRemoteCache(String cacheName, RemoteCache remoteCache) { + remoteCaches.put(cacheName, remoteCache); } public Set getRemoteCacheNames() { @@ -65,8 +64,8 @@ public class RemoteCacheInvoker { public void runTask(KeycloakSession kcSession, RealmModel realm, String cacheName, K key, MergedUpdate task, SessionEntityWrapper sessionWrapper) { - RemoteCacheContext context = remoteCaches.get(cacheName); - if (context == null) { + RemoteCache remoteCache = remoteCaches.get(cacheName); + if (remoteCache == null) { return; } @@ -80,10 +79,7 @@ public class RemoteCacheInvoker { return; } - long loadedMaxIdleTimeMs = context.maxIdleTimeLoader.getMaxIdleTimeMs(realm); - - // Increase the timeout to ensure that entry won't expire on remoteCache in case that write of some entities to remoteCache is postponed (eg. userSession.lastSessionRefresh) - final long maxIdleTimeMs = loadedMaxIdleTimeMs + 1800000; + long maxIdleTimeMs = getMaxIdleTimeMs(task); if (logger.isTraceEnabled()) { logger.tracef("Running task '%s' on remote cache '%s' . Key is '%s'", operation, cacheName, key); @@ -94,7 +90,7 @@ public class RemoteCacheInvoker { Retry.executeWithBackoff((int iteration) -> { try { - runOnRemoteCache(topology, context.remoteCache, maxIdleTimeMs, key, task, sessionWrapper); + runOnRemoteCache(topology, remoteCache, maxIdleTimeMs, key, task, sessionWrapper); } catch (HotRodClientException re) { if (logger.isDebugEnabled()) { logger.debugf(re, "Failed running task '%s' on remote cache '%s' . Key: '%s', iteration '%s'. Will try to retry the task", @@ -108,6 +104,14 @@ public class RemoteCacheInvoker { }, 10, 10); } + private static long getMaxIdleTimeMs(MergedUpdate task) { + long maxIdleTimeMs = task.getMaxIdleTimeMs(); + if (maxIdleTimeMs > 0) { + // Increase the timeout to ensure that entry won't expire on remoteCache in case that write of some entities to remoteCache is postponed (eg. userSession.lastSessionRefresh) + maxIdleTimeMs += 1800000; + } + return maxIdleTimeMs; + } private void runOnRemoteCache(TopologyInfo topology, RemoteCache> remoteCache, long maxIdleMs, K key, MergedUpdate task, SessionEntityWrapper sessionWrapper) { SessionUpdateTask.CacheOperation operation = task.getOperation(); @@ -195,27 +199,4 @@ public class RemoteCacheInvoker { private String logTopologyData(TopologyInfo topology, int iteration) { return topology.toString() + ", replaceIteration: " + iteration; } - - - private static class RemoteCacheContext { - - private final RemoteCache remoteCache; - private final MaxIdleTimeLoader maxIdleTimeLoader; - - public RemoteCacheContext(RemoteCache remoteCache, MaxIdleTimeLoader maxIdleLoader) { - this.remoteCache = remoteCache; - this.maxIdleTimeLoader = maxIdleLoader; - } - - } - - - @FunctionalInterface - public interface MaxIdleTimeLoader { - - long getMaxIdleTimeMs(RealmModel realm); - - } - - }