Add new indices on offline_client_session

Closes #43566
Closes #43516

Signed-off-by: twobiers <22715034+twobiers@users.noreply.github.com>
Signed-off-by: Alexander Schwartz <alexander.schwartz@ibm.com>
Co-authored-by: Alexander Schwartz <alexander.schwartz@ibm.com>
This commit is contained in:
Tobi 2025-10-31 17:49:47 +01:00 committed by Pedro Igor
parent 1f0b5d4cb2
commit 84c5701b89
5 changed files with 82 additions and 6 deletions

View File

@ -8,3 +8,11 @@ Notable changes where an internal behavior changed to prevent common misconfigur
When the "Remember Me" option is disabled in the realm settings, all user sessions previously created with the "Remember Me" flag are now considered invalid.
Users will be required to log in again, and any associated refresh tokens will no longer be usable.
User sessions created without selecting "Remember Me" are not affected.
=== Added database indexes on `OFFLINE_CLIENT_SESSION` table
This adds new indexes on `OFFLINE_CLIENT_SESSION` table to improve performance when retrieving or deleting client sessions.
If those tables contain more than 300000 entries, {project_name} will skip the index creation by default during the automatic schema migration and instead log the SQL statement on the console during migration to be applied manually after {project_name}'s startup.
See the link:{upgradingguide_link}[{upgradingguide_name}] for details on how to configure a different limit.

View File

@ -200,6 +200,12 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
onClientRemoved(client.getId());
}
/**
* Remove client sessions for a specific client.
* <p>
* We need to remove the client sessions for clients that are no longer present, as we would otherwise
* look up those non-existent clients again and again, and this would be slow as they would never be in the cache.
*/
private void onClientRemoved(String clientUUID) {
logger.debugf("Client sessions removed for client %s", clientUUID);
StorageId clientStorageId = new StorageId(clientUUID);

View File

@ -34,8 +34,8 @@ import java.io.Serializable;
*/
@NamedQueries({
@NamedQuery(name="deleteClientSessionsByRealm", query="delete from PersistentClientSessionEntity sess where sess.userSessionId IN (select u.userSessionId from PersistentUserSessionEntity u where u.realmId = :realmId)"),
@NamedQuery(name="deleteClientSessionsByClient", query="delete from PersistentClientSessionEntity sess where sess.clientId = :clientId"),
@NamedQuery(name="deleteClientSessionsByExternalClient", query="delete from PersistentClientSessionEntity sess where sess.clientStorageProvider = :clientStorageProvider and sess.externalClientId = :externalClientId"),
@NamedQuery(name="deleteClientSessionsByClient", query="delete from PersistentClientSessionEntity sess where sess.clientId = :clientId and sess.clientId != 'external'"),
@NamedQuery(name="deleteClientSessionsByExternalClient", query="delete from PersistentClientSessionEntity sess where sess.clientStorageProvider = :clientStorageProvider and sess.externalClientId = :externalClientId and sess.clientStorageProvider != 'internal'"),
@NamedQuery(name="deleteClientSessionsByClientStorageProvider", query="delete from PersistentClientSessionEntity sess where sess.clientStorageProvider = :clientStorageProvider"),
@NamedQuery(name="deleteClientSessionsByUser", query="delete from PersistentClientSessionEntity sess where sess.userSessionId IN (select u.userSessionId from PersistentUserSessionEntity u where u.userId = :userId)"),
@NamedQuery(name="deleteClientSessionsByUserSession", query="delete from PersistentClientSessionEntity sess where sess.userSessionId = :userSessionId and sess.offline = :offline"),
@ -44,10 +44,10 @@ import java.io.Serializable;
@NamedQuery(name="findClientSessionsByUserSession", query="select sess from PersistentClientSessionEntity sess where sess.userSessionId=:userSessionId and sess.offline = :offline"),
@NamedQuery(name="findClientSessionsOrderedByIdInterval", query="select sess from PersistentClientSessionEntity sess where sess.offline = :offline and sess.userSessionId >= :fromSessionId and sess.userSessionId <= :toSessionId order by sess.userSessionId"),
@NamedQuery(name="findClientSessionsOrderedByIdExact", query="select sess from PersistentClientSessionEntity sess where sess.offline = :offline and sess.userSessionId IN (:userSessionIds)"),
@NamedQuery(name="findClientSessionsCountByClient", query="select count(sess) from PersistentClientSessionEntity sess where sess.offline = :offline and sess.clientId = :clientId"),
@NamedQuery(name="findClientSessionsCountByExternalClient", query="select count(sess) from PersistentClientSessionEntity sess where sess.offline = :offline and sess.clientStorageProvider = :clientStorageProvider and sess.externalClientId = :externalClientId"),
@NamedQuery(name="findClientSessionsByUserSessionAndClient", query="select sess from PersistentClientSessionEntity sess where sess.userSessionId=:userSessionId and sess.offline = :offline and sess.clientId=:clientId"),
@NamedQuery(name="findClientSessionsByUserSessionAndExternalClient", query="select sess from PersistentClientSessionEntity sess where sess.userSessionId=:userSessionId and sess.offline = :offline and sess.clientStorageProvider = :clientStorageProvider and sess.externalClientId = :externalClientId")
@NamedQuery(name="findClientSessionsCountByClient", query="select count(sess) from PersistentClientSessionEntity sess where sess.offline = :offline and sess.clientId = :clientId and sess.clientId != 'external'"),
@NamedQuery(name="findClientSessionsCountByExternalClient", query="select count(sess) from PersistentClientSessionEntity sess where sess.offline = :offline and sess.clientStorageProvider = :clientStorageProvider and sess.externalClientId = :externalClientId and sess.clientStorageProvider != 'internal'"),
@NamedQuery(name="findClientSessionsByUserSessionAndClient", query="select sess from PersistentClientSessionEntity sess where sess.userSessionId=:userSessionId and sess.offline = :offline and sess.clientId=:clientId and sess.clientId != 'external'"),
@NamedQuery(name="findClientSessionsByUserSessionAndExternalClient", query="select sess from PersistentClientSessionEntity sess where sess.userSessionId=:userSessionId and sess.offline = :offline and sess.clientStorageProvider = :clientStorageProvider and sess.externalClientId = :externalClientId and sess.clientStorageProvider != 'internal'")
})
@Table(name="OFFLINE_CLIENT_SESSION")
@Entity

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
~ * Copyright 2025 Red Hat, Inc. and/or its affiliates
~ * and other contributors as indicated by the @author tags.
~ *
~ * Licensed under the Apache License, Version 2.0 (the "License");
~ * you may not use this file except in compliance with the License.
~ * You may obtain a copy of the License at
~ *
~ * http://www.apache.org/licenses/LICENSE-2.0
~ *
~ * Unless required by applicable law or agreed to in writing, software
~ * distributed under the License is distributed on an "AS IS" BASIS,
~ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ * See the License for the specific language governing permissions and
~ * limitations under the License.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<!-- These indexes IDX_OFFLINE_CSS_BY_CLIENT and IDX_OFFLINE_CSS_BY_CLIENT_STORAGE_PROVIDER are used to retrieve
sessions by client and to count sessions by client. They are also used to remove client sessions for no-longer
existing clients from the table. -->
<changeSet author="keycloak" id="26.5.0-index-offline-css-by-client">
<preConditions onFail="MARK_RAN" onSqlOutput="TEST">
<not>
<indexExists tableName="OFFLINE_CLIENT_SESSION" indexName="IDX_OFFLINE_CSS_BY_CLIENT" />
</not>
</preConditions>
<createIndex tableName="OFFLINE_CLIENT_SESSION" indexName="IDX_OFFLINE_CSS_BY_CLIENT">
<column name="CLIENT_ID" type="VARCHAR(255)" />
<column name="OFFLINE_FLAG" type="VARCHAR(4)"/>
</createIndex>
<modifySql dbms="postgresql">
<append value="WHERE CLIENT_ID != 'external'" />
</modifySql>
<modifySql dbms="mssql">
<append value="WHERE CLIENT_ID != 'external'" />
</modifySql>
</changeSet>
<changeSet author="keycloak" id="26.5.0-index-offline-css-by-client-storage-provider">
<preConditions onFail="MARK_RAN" onSqlOutput="TEST">
<not>
<indexExists tableName="OFFLINE_CLIENT_SESSION" indexName="IDX_OFFLINE_CSS_BY_CLIENT_STORAGE_PROVIDER" />
</not>
</preConditions>
<createIndex tableName="OFFLINE_CLIENT_SESSION" indexName="IDX_OFFLINE_CSS_BY_CLIENT_STORAGE_PROVIDER">
<column name="CLIENT_STORAGE_PROVIDER" type="VARCHAR(36)" />
<column name="EXTERNAL_CLIENT_ID" type="VARCHAR(255)"/>
<column name="OFFLINE_FLAG" type="VARCHAR(4)"/>
</createIndex>
<modifySql dbms="postgresql">
<append value="WHERE CLIENT_STORAGE_PROVIDER != 'internal'" />
</modifySql>
<modifySql dbms="mssql">
<append value="WHERE CLIENT_STORAGE_PROVIDER != 'internal'" />
</modifySql>
</changeSet>
</databaseChangeLog>

View File

@ -87,5 +87,6 @@
<include file="META-INF/jpa-changelog-26.1.0.xml"/>
<include file="META-INF/jpa-changelog-26.2.0.xml"/>
<include file="META-INF/jpa-changelog-26.2.6.xml"/>
<include file="META-INF/jpa-changelog-26.5.0.xml"/>
</databaseChangeLog>