Do not use LastSessionRefreshPersister with persistent user sessions enabled

Closes #29144

Signed-off-by: Michal Hajas <mhajas@redhat.com>
This commit is contained in:
Michal Hajas 2024-05-02 12:55:11 +02:00 committed by Alexander Schwartz
parent ba43a10a6d
commit 8b715d3a31
6 changed files with 28 additions and 24 deletions

View file

@ -126,7 +126,6 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
remoteCacheInvoker,
lastSessionRefreshStore,
offlineLastSessionRefreshStore,
persisterLastSessionRefreshStore,
keyGenerator,
cache,
offlineSessionsCache,
@ -178,7 +177,9 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
keyGenerator = new InfinispanKeyGenerator();
checkRemoteCaches(session);
loadPersistentSessions(factory, getMaxErrors(), getSessionsPerSegment());
if (!Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
initializeLastSessionRefreshStore(factory);
}
registerClusterListeners(session);
loadSessionsFromRemoteCaches(session);
@ -234,9 +235,7 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
return config.getInt("sessionPreloadStalledTimeoutInSeconds", defaultTimeout);
}
@Override
public void loadPersistentSessions(final KeycloakSessionFactory sessionFactory, final int maxErrors, final int sessionsPerSegment) {
public void initializeLastSessionRefreshStore(final KeycloakSessionFactory sessionFactory) {
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
@Override
@ -244,10 +243,7 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
// Initialize persister for periodically doing bulk DB updates of lastSessionRefresh timestamps of refreshed sessions
persisterLastSessionRefreshStore = new PersisterLastSessionRefreshStoreFactory().createAndInit(session, true);
}
});
}

View file

@ -107,7 +107,6 @@ public class PersistentUserSessionProvider implements UserSessionProvider, Sessi
protected final CrossDCLastSessionRefreshStore lastSessionRefreshStore;
protected final CrossDCLastSessionRefreshStore offlineLastSessionRefreshStore;
protected final PersisterLastSessionRefreshStore persisterLastSessionRefreshStore;
protected final RemoteCacheInvoker remoteCacheInvoker;
protected final InfinispanKeyGenerator keyGenerator;
@ -116,7 +115,6 @@ public class PersistentUserSessionProvider implements UserSessionProvider, Sessi
RemoteCacheInvoker remoteCacheInvoker,
CrossDCLastSessionRefreshStore lastSessionRefreshStore,
CrossDCLastSessionRefreshStore offlineLastSessionRefreshStore,
PersisterLastSessionRefreshStore persisterLastSessionRefreshStore,
InfinispanKeyGenerator keyGenerator,
Cache<String, SessionEntityWrapper<UserSessionEntity>> sessionCache,
Cache<String, SessionEntityWrapper<UserSessionEntity>> offlineSessionCache,
@ -153,7 +151,6 @@ public class PersistentUserSessionProvider implements UserSessionProvider, Sessi
this.lastSessionRefreshStore = lastSessionRefreshStore;
this.offlineLastSessionRefreshStore = offlineLastSessionRefreshStore;
this.persisterLastSessionRefreshStore = persisterLastSessionRefreshStore;
this.remoteCacheInvoker = remoteCacheInvoker;
this.keyGenerator = keyGenerator;
@ -182,7 +179,7 @@ public class PersistentUserSessionProvider implements UserSessionProvider, Sessi
@Override
public PersisterLastSessionRefreshStore getPersisterLastSessionRefreshStore() {
return persisterLastSessionRefreshStore;
throw new IllegalStateException("PersisterLastSessionRefreshStore is not supported in PersistentUserSessionProvider");
}
@Override

View file

@ -17,6 +17,7 @@
package org.keycloak.models.sessions.infinispan;
import org.keycloak.common.Profile;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
@ -230,7 +231,7 @@ public class UserSessionAdapter<T extends SessionRefreshStore & UserSessionProvi
return;
}
if (offline) {
if (!Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS) && offline) {
// Received the message from the other DC that we should update the lastSessionRefresh in local cluster. Don't update DB in that case.
// The other DC already did.
Boolean ignoreRemoteCacheUpdate = (Boolean) session.getAttribute(CrossDCLastSessionRefreshListener.IGNORE_REMOTE_CACHE_UPDATE);

View file

@ -26,6 +26,8 @@ import org.keycloak.provider.ProviderFactory;
public interface UserSessionProviderFactory<T extends UserSessionProvider> extends ProviderFactory<T> {
// This is supposed to prefill all userSessions and clientSessions from userSessionPersister to the userSession infinispan/memory storage
void loadPersistentSessions(KeycloakSessionFactory sessionFactory, final int maxErrors, final int sessionsPerSegment);
// This method is no longer used as we don't have offline sessions preloading anymore
@Deprecated
default void loadPersistentSessions(KeycloakSessionFactory sessionFactory, final int maxErrors, final int sessionsPerSegment) {}
}

View file

@ -19,6 +19,7 @@ package org.keycloak.testsuite.model.session;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;
import org.keycloak.common.Profile;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
@ -119,15 +120,16 @@ public class UserSessionProviderModelTest extends KeycloakModelTest {
@Test
public void testExpiredClientSessions() {
// Suspend periodic tasks to avoid race-conditions, which may cause missing updates of lastSessionRefresh times to UserSessionPersisterProvider
// skip for persistent user sessions as the periodic task is not used there
TimerProvider timer = kcSession.getProvider(TimerProvider.class);
TimerProvider.TimerTaskContext timerTaskCtx = null;
if (timer != null) {
if (timer != null && !Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
timerTaskCtx = timer.cancelTask(PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
log.info("Cancelled periodic task " + PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
InfinispanTestUtil.setTestingTimeService(kcSession);
}
InfinispanTestUtil.setTestingTimeService(kcSession);
try {
UserSessionModel[] origSessions = inComittedTransaction(session -> {
// create some user and client sessions
@ -172,11 +174,12 @@ public class UserSessionProviderModelTest extends KeycloakModelTest {
} finally {
setTimeOffset(0);
kcSession.getKeycloakSessionFactory().publish(new ResetTimeOffsetEvent());
if (timer != null && timerTaskCtx != null) {
// Enable periodic task again, skip for persistent user sessions as the periodic task is not used there
if (timer != null && timerTaskCtx != null && !Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
timer.schedule(timerTaskCtx.getRunnable(), timerTaskCtx.getIntervalMillis(), PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
InfinispanTestUtil.revertTimeService(kcSession);
}
InfinispanTestUtil.revertTimeService(kcSession);
}
}

View file

@ -121,9 +121,10 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest {
@Test
public void testExpired() {
// Suspend periodic tasks to avoid race-conditions, which may cause missing updates of lastSessionRefresh times to UserSessionPersisterProvider
// skip for persistent user sessions as the periodic task is not used there
TimerProvider timer = kcSession.getProvider(TimerProvider.class);
TimerProvider.TimerTaskContext timerTaskCtx = null;
if (timer != null) {
if (timer != null && !Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
timerTaskCtx = timer.cancelTask(PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
log.info("Cancelled periodic task " + PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
}
@ -236,7 +237,8 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest {
} finally {
setTimeOffset(0);
kcSession.getKeycloakSessionFactory().publish(new ResetTimeOffsetEvent());
if (timer != null) {
// Enable periodic task again, skip for persistent user sessions as the periodic task is not used there
if (timer != null && !Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
timer.schedule(timerTaskCtx.getRunnable(), timerTaskCtx.getIntervalMillis(), PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
}
@ -247,9 +249,10 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest {
@Test
public void testLoadUserSessionsWithNotDeletedOfflineClientSessions() {
// Suspend periodic tasks to avoid race-conditions, which may cause missing updates of lastSessionRefresh times to UserSessionPersisterProvider
// skip for persistent user sessions as the periodic task is not used there
TimerProvider timer = kcSession.getProvider(TimerProvider.class);
TimerProvider.TimerTaskContext timerTaskCtx = null;
if (timer != null) {
if (timer != null && !Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
timerTaskCtx = timer.cancelTask(PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
log.info("Cancelled periodic task " + PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
}
@ -318,7 +321,9 @@ public class UserSessionProviderOfflineModelTest extends KeycloakModelTest {
} finally {
setTimeOffset(0);
kcSession.getKeycloakSessionFactory().publish(new ResetTimeOffsetEvent());
if (timer != null) {
// Enable periodic task again, skip for persistent user sessions as the periodic task is not used there
if (timer != null && !Profile.isFeatureEnabled(Profile.Feature.PERSISTENT_USER_SESSIONS)) {
timer.schedule(timerTaskCtx.getRunnable(), timerTaskCtx.getIntervalMillis(), PersisterLastSessionRefreshStoreFactory.DB_LSR_PERIODIC_TASK_NAME);
}