Add new indices on offline_client_session

Closes #43566

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 GitHub
parent 41d5aae6f6
commit 479859a7a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 81 additions and 6 deletions

View File

@ -103,6 +103,13 @@ make sure the users granted with the `realm-admin` role are expected to have thi
The documentation is also updated with additional information about the different types of realm administrators.
For more information, see link:{adminguide_link}#_fine_grained_permissions[Delegating realm administration using permissions].
=== 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.
// ------------------------ Deprecated features ------------------------ //
== Deprecated features

View File

@ -196,6 +196,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

@ -89,5 +89,6 @@
<include file="META-INF/jpa-changelog-26.2.6.xml"/>
<include file="META-INF/jpa-changelog-26.3.0.xml"/>
<include file="META-INF/jpa-changelog-26.4.0.xml"/>
<include file="META-INF/jpa-changelog-26.5.0.xml"/>
</databaseChangeLog>