Merge pull request #4451 from glavoie/KEYCLOAK-4858-ResourceServer

KEYCLOAK-4858: Slow query performance for client with large data volume
This commit is contained in:
Pedro Igor authored and GitHub committed 2017-09-12 15:54:16 -03:00
commit 90db6654d3
44 files changed
+181 -195

No files matched your search

@@ -38,7 +38,7 @@ public class ResourceServerAdapter implements ResourceServer, CachedModel<Resour
@Override
public ResourceServer getDelegateForUpdate() {
if (updated == null) {
cacheSession.registerResourceServerInvalidation(cached.getId(), cached.getClientId());
cacheSession.registerResourceServerInvalidation(cached.getId());
updated = cacheSession.getResourceServerStoreDelegate().findById(cached.getId());
if (updated == null) throw new IllegalStateException("Not found in database");
}
@@ -78,12 +78,6 @@ public class ResourceServerAdapter implements ResourceServer, CachedModel<Resour
return cached.getId();
}
@Override
public String getClientId() {
if (isUpdated()) return updated.getClientId();
return cached.getClientId();
}
@Override
public boolean isAllowRemoteResourceManagement() {
if (isUpdated()) return updated.isAllowRemoteResourceManagement();
@@ -53,13 +53,13 @@ public class StoreFactoryCacheManager extends CacheManager {
}
}
public void resourceServerUpdated(String id, String clientId, Set<String> invalidations) {
public void resourceServerUpdated(String id, Set<String> invalidations) {
invalidations.add(id);
invalidations.add(StoreFactoryCacheSession.getResourceServerByClientCacheKey(clientId));
invalidations.add(StoreFactoryCacheSession.getResourceServerByClientCacheKey(id));
}
public void resourceServerRemoval(String id, String name, Set<String> invalidations) {
resourceServerUpdated(id, name, invalidations);
public void resourceServerRemoval(String id, Set<String> invalidations) {
resourceServerUpdated(id, invalidations);
addInvalidations(InResourceServerPredicate.create().resourceServer(id), invalidations);
}
@@ -229,12 +229,12 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
return invalidations.contains(id);
}
public void registerResourceServerInvalidation(String id, String clientId) {
cache.resourceServerUpdated(id, clientId, invalidations);
public void registerResourceServerInvalidation(String id) {
cache.resourceServerUpdated(id, invalidations);
ResourceServerAdapter adapter = managedResourceServers.get(id);
if (adapter != null) adapter.invalidateFlag();
invalidationEvents.add(ResourceServerUpdatedEvent.create(id, clientId));
invalidationEvents.add(ResourceServerUpdatedEvent.create(id));
}
public void registerScopeInvalidation(String id, String name, String serverId) {
@@ -350,7 +350,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
@Override
public ResourceServer create(String clientId) {
ResourceServer server = getResourceServerStoreDelegate().create(clientId);
registerResourceServerInvalidation(server.getId(), server.getClientId());
registerResourceServerInvalidation(server.getId());
return server;
}
@@ -361,8 +361,8 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
if (server == null) return;
cache.invalidateObject(id);
invalidationEvents.add(ResourceServerRemovedEvent.create(id, server.getClientId()));
cache.resourceServerRemoval(id, server.getClientId(), invalidations);
invalidationEvents.add(ResourceServerRemovedEvent.create(id, server.getId()));
cache.resourceServerRemoval(id, invalidations);
getResourceServerStoreDelegate().delete(id);
}
@@ -392,33 +392,6 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
managedResourceServers.put(id, adapter);
return adapter;
}
@Override
public ResourceServer findByClient(String clientId) {
String cacheKey = getResourceServerByClientCacheKey(clientId);
ResourceServerListQuery query = cache.get(cacheKey, ResourceServerListQuery.class);
if (query != null) {
logger.tracev("ResourceServer by clientId cache hit: {0}", clientId);
}
if (query == null) {
Long loaded = cache.getCurrentRevision(cacheKey);
ResourceServer model = getResourceServerStoreDelegate().findByClient(clientId);
if (model == null) return null;
if (invalidations.contains(model.getId())) return model;
query = new ResourceServerListQuery(loaded, cacheKey, model.getId());
cache.addRevisioned(query, startupRevision);
return model;
} else if (invalidations.contains(cacheKey)) {
return getResourceServerStoreDelegate().findByClient(clientId);
} else {
String serverId = query.getResourceServers().iterator().next();
if (invalidations.contains(serverId)) {
return getResourceServerStoreDelegate().findByClient(clientId);
}
return findById(serverId);
}
}
}
protected class ScopeCache implements ScopeStore {
@@ -22,29 +22,20 @@ import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned;
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
import java.io.Serializable;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class CachedResourceServer extends AbstractRevisioned {
private String clientId;
private boolean allowRemoteResourceManagement;
private PolicyEnforcementMode policyEnforcementMode;
public CachedResourceServer(Long revision, ResourceServer resourceServer) {
super(revision, resourceServer.getId());
this.clientId = resourceServer.getClientId();
this.allowRemoteResourceManagement = resourceServer.isAllowRemoteResourceManagement();
this.policyEnforcementMode = resourceServer.getPolicyEnforcementMode();
}
public String getClientId() {
return this.clientId;
}
public boolean isAllowRemoteResourceManagement() {
return this.allowRemoteResourceManagement;
}
@@ -49,6 +49,6 @@ public class ResourceServerRemovedEvent extends InvalidationEvent implements Aut
@Override
public void addInvalidations(StoreFactoryCacheManager cache, Set<String> invalidations) {
cache.resourceServerRemoval(id, clientId, invalidations);
cache.resourceServerRemoval(id, invalidations);
}
}
@@ -28,12 +28,10 @@ import java.util.Set;
public class ResourceServerUpdatedEvent extends InvalidationEvent implements AuthorizationCacheInvalidationEvent {
private String id;
private String clientId;
public static ResourceServerUpdatedEvent create(String id, String clientId) {
public static ResourceServerUpdatedEvent create(String id) {
ResourceServerUpdatedEvent event = new ResourceServerUpdatedEvent();
event.id = id;
event.clientId = clientId;
return event;
}
@@ -44,11 +42,11 @@ public class ResourceServerUpdatedEvent extends InvalidationEvent implements Aut
@Override
public String toString() {
return String.format("ResourceServerRemovedEvent [ id=%s, clientId=%s ]", id, clientId);
return String.format("ResourceServerRemovedEvent [ id=%s, clientId=%s ]", id, id);
}
@Override
public void addInvalidations(StoreFactoryCacheManager cache, Set<String> invalidations) {
cache.resourceServerUpdated(id, clientId, invalidations);
cache.resourceServerUpdated(id, invalidations);
}
}
@@ -18,41 +18,24 @@
package org.keycloak.authorization.jpa.entities;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import java.util.List;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
@Entity
@Table(name = "RESOURCE_SERVER", uniqueConstraints = {@UniqueConstraint(columnNames = "CLIENT_ID")})
@NamedQueries(
{
@NamedQuery(name="findResourceServerIdByClient", query="select r.id from ResourceServerEntity r where r.clientId = :clientId"),
}
)
@Table(name = "RESOURCE_SERVER")
public class ResourceServerEntity {
@Id
@Column(name="ID", length = 36)
@Access(AccessType.PROPERTY) // we do this because relationships often fetch id, but not entity. This avoids an extra SQL
private String id;
@Column(name = "CLIENT_ID")
private String clientId;
@Column(name = "ALLOW_RS_REMOTE_MGMT")
private boolean allowRemoteResourceManagement;
@@ -67,14 +50,6 @@ public class ResourceServerEntity {
this.id = id;
}
public String getClientId() {
return this.clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public boolean isAllowRemoteResourceManagement() {
return this.allowRemoteResourceManagement;
}
@@ -22,16 +22,11 @@ import org.keycloak.authorization.jpa.entities.PolicyEntity;
import org.keycloak.authorization.jpa.entities.ResourceEntity;
import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
import org.keycloak.authorization.jpa.entities.ScopeEntity;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.store.ResourceServerStore;
import org.keycloak.models.utils.KeycloakModelUtils;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import java.util.LinkedList;
import java.util.List;
@@ -53,8 +48,7 @@ public class JPAResourceServerStore implements ResourceServerStore {
public ResourceServer create(String clientId) {
ResourceServerEntity entity = new ResourceServerEntity();
entity.setId(KeycloakModelUtils.generateId());
entity.setClientId(clientId);
entity.setId(clientId);
this.entityManager.persist(entity);
@@ -116,17 +110,4 @@ public class JPAResourceServerStore implements ResourceServerStore {
if (entity == null) return null;
return new ResourceServerAdapter(entity, entityManager, provider.getStoreFactory());
}
@Override
public ResourceServer findByClient(final String clientId) {
TypedQuery<String> query = entityManager.createNamedQuery("findResourceServerIdByClient", String.class);
query.setParameter("clientId", clientId);
try {
String id = query.getSingleResult();
return provider.getStoreFactory().getResourceServerStore().findById(id);
} catch (NoResultException ex) {
return null;
}
}
}
@@ -16,7 +16,6 @@
*/
package org.keycloak.authorization.jpa.store;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.jpa.entities.PolicyEntity;
import org.keycloak.authorization.jpa.entities.ResourceEntity;
import org.keycloak.authorization.jpa.entities.ScopeEntity;
@@ -16,7 +16,6 @@
*/
package org.keycloak.authorization.jpa.store;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.jpa.entities.ResourceEntity;
import org.keycloak.authorization.jpa.entities.ScopeEntity;
import org.keycloak.authorization.model.Resource;
@@ -16,11 +16,7 @@
*/
package org.keycloak.authorization.jpa.store;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.jpa.entities.ResourceEntity;
import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.jpa.JpaModel;
@@ -53,11 +49,6 @@ public class ResourceServerAdapter implements ResourceServer, JpaModel<ResourceS
return entity.getId();
}
@Override
public String getClientId() {
return entity.getClientId();
}
@Override
public boolean isAllowRemoteResourceManagement() {
return entity.isAllowRemoteResourceManagement();
@@ -16,12 +16,10 @@
*/
package org.keycloak.authorization.jpa.store;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.jpa.entities.ScopeEntity;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.jpa.JpaModel;
import javax.persistence.EntityManager;
@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
~ * Copyright 2017 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.2.xsd">
<changeSet author="glavoie@gmail.com" id="authz-3.4.0.CR1-resource-server-pk-change-part1">
<!-- Data migration to change the PK of RESOURCE_SERVER to use the CLIENT_ID. -->
<addColumn tableName="RESOURCE_SERVER_POLICY">
<column name="RESOURCE_SERVER_CLIENT_ID" type="VARCHAR(36)"/>
</addColumn>
<addColumn tableName="RESOURCE_SERVER_RESOURCE">
<column name="RESOURCE_SERVER_CLIENT_ID" type="VARCHAR(36)"/>
</addColumn>
<addColumn tableName="RESOURCE_SERVER_SCOPE">
<column name="RESOURCE_SERVER_CLIENT_ID" type="VARCHAR(36)"/>
</addColumn>
</changeSet>
<changeSet author="glavoie@gmail.com" id="authz-3.4.0.CR1-resource-server-pk-change-part2">
<preConditions onSqlOutput="TEST" onFail="MARK_RAN">
<not>
<dbms type="mssql" />
</not>
</preConditions>
<sql>
UPDATE RESOURCE_SERVER_POLICY p SET RESOURCE_SERVER_CLIENT_ID = (SELECT CLIENT_ID FROM RESOURCE_SERVER s WHERE s.ID = p.RESOURCE_SERVER_ID);
UPDATE RESOURCE_SERVER_RESOURCE p SET RESOURCE_SERVER_CLIENT_ID = (SELECT CLIENT_ID FROM RESOURCE_SERVER s WHERE s.ID = p.RESOURCE_SERVER_ID);
UPDATE RESOURCE_SERVER_SCOPE p SET RESOURCE_SERVER_CLIENT_ID = (SELECT CLIENT_ID FROM RESOURCE_SERVER s WHERE s.ID = p.RESOURCE_SERVER_ID);
</sql>
</changeSet>
<changeSet author="glavoie@gmail.com" id="authz-3.4.0.CR1-resource-server-pk-change-part2-mssql">
<preConditions onSqlOutput="TEST" onFail="MARK_RAN">
<dbms type="mssql" />
</preConditions>
<sql>
UPDATE RESOURCE_SERVER_POLICY SET RESOURCE_SERVER_CLIENT_ID = s.CLIENT_ID FROM (SELECT ID, CLIENT_ID FROM RESOURCE_SERVER) s WHERE s.ID = RESOURCE_SERVER_POLICY.RESOURCE_SERVER_ID;
UPDATE RESOURCE_SERVER_RESOURCE SET RESOURCE_SERVER_CLIENT_ID = s.CLIENT_ID FROM (SELECT ID, CLIENT_ID FROM RESOURCE_SERVER) s WHERE s.ID = RESOURCE_SERVER_RESOURCE.RESOURCE_SERVER_ID;
UPDATE RESOURCE_SERVER_SCOPE SET RESOURCE_SERVER_CLIENT_ID = s.CLIENT_ID FROM (SELECT ID, CLIENT_ID FROM RESOURCE_SERVER) s WHERE s.ID = RESOURCE_SERVER_SCOPE.RESOURCE_SERVER_ID;
</sql>
</changeSet>
<changeSet author="glavoie@gmail.com" id="authz-3.4.0.CR1-resource-server-pk-change-part3">
<addNotNullConstraint tableName="RESOURCE_SERVER_POLICY" columnName="RESOURCE_SERVER_CLIENT_ID" columnDataType="VARCHAR(36)"/>
<addNotNullConstraint tableName="RESOURCE_SERVER_RESOURCE" columnName="RESOURCE_SERVER_CLIENT_ID" columnDataType="VARCHAR(36)"/>
<addNotNullConstraint tableName="RESOURCE_SERVER_SCOPE" columnName="RESOURCE_SERVER_CLIENT_ID" columnDataType="VARCHAR(36)"/>
<dropUniqueConstraint tableName="RESOURCE_SERVER_POLICY" constraintName="UK_FRSRPT700S9V50BU18WS5HA6"/>
<dropUniqueConstraint tableName="RESOURCE_SERVER_RESOURCE" constraintName="UK_FRSR6T700S9V50BU18WS5HA6"/>
<dropUniqueConstraint tableName="RESOURCE_SERVER_SCOPE" constraintName="UK_FRSRST700S9V50BU18WS5HA6"/>
<dropForeignKeyConstraint baseTableName="RESOURCE_SERVER_POLICY" constraintName="FK_FRSRPO213XCX4WNKOG82SSRFY"/>
<dropIndex tableName="RESOURCE_SERVER_POLICY" indexName="IDX_RES_SERV_POL_RES_SERV"/>
<dropColumn tableName="RESOURCE_SERVER_POLICY" columnName="RESOURCE_SERVER_ID"/>
<dropForeignKeyConstraint baseTableName="RESOURCE_SERVER_RESOURCE" constraintName="FK_FRSRHO213XCX4WNKOG82SSRFY"/>
<dropIndex tableName="RESOURCE_SERVER_RESOURCE" indexName="IDX_RES_SRV_RES_RES_SRV"/>
<dropColumn tableName="RESOURCE_SERVER_RESOURCE" columnName="RESOURCE_SERVER_ID"/>
<dropForeignKeyConstraint baseTableName="RESOURCE_SERVER_SCOPE" constraintName="FK_FRSRSO213XCX4WNKOG82SSRFY"/>
<dropIndex tableName="RESOURCE_SERVER_SCOPE" indexName="IDX_RES_SRV_SCOPE_RES_SRV"/>
<dropColumn tableName="RESOURCE_SERVER_SCOPE" columnName="RESOURCE_SERVER_ID"/>
<dropPrimaryKey tableName="RESOURCE_SERVER" constraintName="CONSTRAINT_FARS"/>
<dropUniqueConstraint tableName="RESOURCE_SERVER" constraintName="UK_AU8TT6T700S9V50BU18WS5HA6"/>
<dropColumn tableName="RESOURCE_SERVER" columnName="ID"/>
<renameColumn tableName="RESOURCE_SERVER" oldColumnName="CLIENT_ID" newColumnName="ID" columnDataType="VARCHAR(36)"/>
<renameColumn tableName="RESOURCE_SERVER_POLICY" oldColumnName="RESOURCE_SERVER_CLIENT_ID" newColumnName="RESOURCE_SERVER_ID" columnDataType="VARCHAR(36)"/>
<renameColumn tableName="RESOURCE_SERVER_RESOURCE" oldColumnName="RESOURCE_SERVER_CLIENT_ID" newColumnName="RESOURCE_SERVER_ID" columnDataType="VARCHAR(36)"/>
<renameColumn tableName="RESOURCE_SERVER_SCOPE" oldColumnName="RESOURCE_SERVER_CLIENT_ID" newColumnName="RESOURCE_SERVER_ID" columnDataType="VARCHAR(36)"/>
<addUniqueConstraint tableName="RESOURCE_SERVER_POLICY" constraintName="UK_FRSRPT700S9V50BU18WS5HA6"
columnNames="NAME, RESOURCE_SERVER_ID"/>
<addUniqueConstraint tableName="RESOURCE_SERVER_RESOURCE" constraintName="UK_FRSR6T700S9V50BU18WS5HA6"
columnNames="NAME, OWNER, RESOURCE_SERVER_ID"/>
<addUniqueConstraint tableName="RESOURCE_SERVER_SCOPE" constraintName="UK_FRSRST700S9V50BU18WS5HA6"
columnNames="NAME, RESOURCE_SERVER_ID"/>
<createIndex indexName="IDX_RES_SERV_POL_RES_SERV" tableName="RESOURCE_SERVER_POLICY">
<column name="RESOURCE_SERVER_ID" type="VARCHAR(36)"/>
</createIndex>
<createIndex indexName="IDX_RES_SRV_RES_RES_SRV" tableName="RESOURCE_SERVER_RESOURCE">
<column name="RESOURCE_SERVER_ID" type="VARCHAR(36)"/>
</createIndex>
<createIndex indexName="IDX_RES_SRV_SCOPE_RES_SRV" tableName="RESOURCE_SERVER_SCOPE">
<column name="RESOURCE_SERVER_ID" type="VARCHAR(36)"/>
</createIndex>
<addPrimaryKey tableName="RESOURCE_SERVER" constraintName="PK_RESOURCE_SERVER" columnNames="ID"/>
<addForeignKeyConstraint constraintName="FK_FRSRPO213XCX4WNKOG82SSRFY"
baseTableName="RESOURCE_SERVER_POLICY" baseColumnNames="RESOURCE_SERVER_ID"
referencedTableName="RESOURCE_SERVER" referencedColumnNames="ID"/>
<addForeignKeyConstraint constraintName="FK_FRSRHO213XCX4WNKOG82SSRFY"
baseTableName="RESOURCE_SERVER_RESOURCE" baseColumnNames="RESOURCE_SERVER_ID"
referencedTableName="RESOURCE_SERVER" referencedColumnNames="ID"/>
<addForeignKeyConstraint constraintName="FK_FRSRSO213XCX4WNKOG82SSRFY"
baseTableName="RESOURCE_SERVER_SCOPE" baseColumnNames="RESOURCE_SERVER_ID"
referencedTableName="RESOURCE_SERVER" referencedColumnNames="ID"/>
</changeSet>
</databaseChangeLog>
@@ -49,4 +49,5 @@
<include file="META-INF/jpa-changelog-3.0.0.xml"/>
<include file="META-INF/jpa-changelog-3.2.0.xml"/>
<include file="META-INF/jpa-changelog-3.3.0.xml"/>
<include file="META-INF/jpa-changelog-authz-3.4.0.CR1.xml"/>
</databaseChangeLog>