From 254483bc5da57e1c0dd3fd76f9d903bcc904ed51 Mon Sep 17 00:00:00 2001 From: mposolda Date: Tue, 9 Aug 2022 21:22:52 +0200 Subject: [PATCH] Use separate transactions for each bulk update of offline sessions in PersisterLastSessionRefreshStore to avoid deadlocks closes #13684 --- .../PersisterLastSessionRefreshStore.java | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/sessions/PersisterLastSessionRefreshStore.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/sessions/PersisterLastSessionRefreshStore.java index cecc4179710a..efb1865a93d0 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/sessions/PersisterLastSessionRefreshStore.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/changes/sessions/PersisterLastSessionRefreshStore.java @@ -19,6 +19,7 @@ import java.util.Map; import java.util.Set; +import java.util.TreeSet; import java.util.stream.Collectors; import org.jboss.logging.Logger; @@ -26,6 +27,7 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.session.UserSessionPersisterProvider; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.SessionTimeoutHelper; /** @@ -59,19 +61,19 @@ protected void sendMessage(KeycloakSession kcSession, Map r logger.debugf("Updating %d userSessions with lastSessionRefresh: %d", refreshesToSend.size(), lastSessionRefresh); } - UserSessionPersisterProvider persister = kcSession.getProvider(UserSessionPersisterProvider.class); - + // Separate transaction for each bulk update request to avoid deadlocks for (Map.Entry> entry : sessionIdsByRealm.entrySet()) { - RealmModel realm = kcSession.realms().getRealm(entry.getKey()); - - // Case when realm was deleted in the meantime. UserSessions were already deleted as well (callback for realm deletion) - if (realm == null) { - continue; - } + KeycloakModelUtils.runJobInTransaction(kcSession.getKeycloakSessionFactory(), (kcSession2) -> { + UserSessionPersisterProvider persister = kcSession2.getProvider(UserSessionPersisterProvider.class); + RealmModel realm = kcSession2.realms().getRealm(entry.getKey()); - Set userSessionIds = entry.getValue(); + // If realm is null, it means that realm was deleted in the meantime. UserSessions were already deleted as well (callback for realm deletion) + if (realm != null) { + Set userSessionIds = new TreeSet<>(entry.getValue()); - persister.updateLastSessionRefreshes(realm, lastSessionRefresh, userSessionIds, offline); + persister.updateLastSessionRefreshes(realm, lastSessionRefresh, userSessionIds, offline); + } + }); } } }