From 6ab411b2de91b30b46cd2ccabea1ce90c1a129d6 Mon Sep 17 00:00:00 2001 From: mposolda Date: Wed, 30 Mar 2016 16:45:15 +0200 Subject: [PATCH 1/2] KEYCLOAK-2610 More operations added to ManyUsersTest --- .../resources/META-INF/keycloak-server.json | 3 +- .../tests/other/jpa-performance/README.md | 4 + .../testsuite/user/ManyUsersTest.java | 112 +++++++++++++++++- .../src/test/resources/log4j.properties | 61 ++++++++++ 4 files changed, 175 insertions(+), 5 deletions(-) create mode 100644 testsuite/integration-arquillian/tests/other/jpa-performance/src/test/resources/log4j.properties diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json index f5d64a511a..9208927bc5 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json @@ -84,7 +84,8 @@ "password": "${keycloak.connectionsJpa.password:}", "databaseSchema": "${keycloak.connectionsJpa.databaseSchema:update}", "showSql": "${keycloak.connectionsJpa.showSql:false}", - "formatSql": "${keycloak.connectionsJpa.formatSql:true}" + "formatSql": "${keycloak.connectionsJpa.formatSql:true}", + "globalStatsInterval": "${keycloak.connectionsJpa.globalStatsInterval:-1}" } }, diff --git a/testsuite/integration-arquillian/tests/other/jpa-performance/README.md b/testsuite/integration-arquillian/tests/other/jpa-performance/README.md index 238bd4cfec..9e05577052 100644 --- a/testsuite/integration-arquillian/tests/other/jpa-performance/README.md +++ b/testsuite/integration-arquillian/tests/other/jpa-performance/README.md @@ -20,6 +20,10 @@ Optional parameters: * `many.users.batch` - Measurement batch size. Default: *1000*. * `many.users.reimport` - Switch for phases 2 and 3. Default: *false*. * `many.users.minTokenValidity` - Minimum validity of admin-client's access token. Default: *10000*. (ms) +* `many.users.read.after.create` - If true, then additional request to read user is send always after the user is created. Default: *false* +* `many.users.create.objects` - If true, then some additional objects will be added to each user (2 attributes, password credential, 2 group mappings, 1 required action) Default: *false* +* `many.users.create.social.links` - If true, then one social (identityProvider) link will be added to each user and then later read. Default: *false* +* `keycloak.connectionsJpa.globalStatsInterval` - Interval in seconds to log Hibernate statistics into log. Default: *-1* (which means statistics are disabled) ### With MySQL diff --git a/testsuite/integration-arquillian/tests/other/jpa-performance/src/test/java/org/keycloak/testsuite/user/ManyUsersTest.java b/testsuite/integration-arquillian/tests/other/jpa-performance/src/test/java/org/keycloak/testsuite/user/ManyUsersTest.java index 914f748555..bbbb9d306b 100644 --- a/testsuite/integration-arquillian/tests/other/jpa-performance/src/test/java/org/keycloak/testsuite/user/ManyUsersTest.java +++ b/testsuite/integration-arquillian/tests/other/jpa-performance/src/test/java/org/keycloak/testsuite/user/ManyUsersTest.java @@ -4,12 +4,29 @@ import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.ws.rs.core.Response; + import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.keycloak.admin.client.resource.UsersResource; +import org.keycloak.authentication.requiredactions.UpdatePassword; +import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserModel; +import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.representations.idm.FederatedIdentityRepresentation; +import org.keycloak.representations.idm.GroupRepresentation; +import org.keycloak.representations.idm.RequiredActionProviderRepresentation; import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.util.Timer; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.util.JsonSerialization; @@ -25,6 +42,16 @@ public class ManyUsersTest extends AbstractUserTest { private static final int COUNT = Integer.parseInt(System.getProperty("many.users.count", "10000")); private static final int BATCH = Integer.parseInt(System.getProperty("many.users.batch", "1000")); + + // When true, then it will always send another request to GET user after he is created (this trigger some DB queries and cache user on Keycloak side) + private static final boolean READ_USER_AFTER_CREATE = Boolean.parseBoolean(System.getProperty("many.users.read.after.create", "false")); + + // When true, then each user will be updated with password, 2 additional attributes, 2 default groups and some required action + private static final boolean CREATE_OBJECTS = Boolean.parseBoolean(System.getProperty("many.users.create.objects", "false")); + + // When true, then each user will be updated with 2 federated identity links + private static final boolean CREATE_SOCIAL_LINKS = Boolean.parseBoolean(System.getProperty("many.users.create.social.links", "false")); + private static final boolean REIMPORT = Boolean.parseBoolean(System.getProperty("many.users.reimport", "false")); private static final String REALM = "realm_with_many_users"; @@ -60,25 +87,90 @@ public class ManyUsersTest extends AbstractUserTest { @Before public void before() { + log.infof("Reading users after create is %s", READ_USER_AFTER_CREATE ? "ENABLED" : "DISABLED"); + users = new LinkedList<>(); for (int i = 0; i < COUNT; i++) { users.add(createUserRep("user" + i)); } realmTimer.reset("create realm before test"); - RealmRepresentation realm = new RealmRepresentation(); - realm.setRealm(REALM); - realmsResouce().create(realm); + createRealm(REALM); + + if (CREATE_OBJECTS) { + + // Assuming default groups and required action already created + if (realmResource().getDefaultGroups().size() == 0) { + log.infof("Creating default groups 'group1' and 'group2'."); + setDefaultGroup("group1"); + setDefaultGroup("group2"); + + RequiredActionProviderRepresentation updatePassword = realmResource().flows().getRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString()); + updatePassword.setDefaultAction(true); + realmResource().flows().updateRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString(), updatePassword); + } + } refreshToken(); } + private void setDefaultGroup(String groupName) { + GroupRepresentation group = new GroupRepresentation(); + group.setName(groupName); + Response resp = realmResource().groups().add(group); + String groupId = ApiUtil.getCreatedId(resp); + resp.close(); + realmResource().addDefaultGroup(groupId); + } + + + @After public void after() { realmTimer.clearStats(true, true, false); usersTimer.clearStats(); } + + @Override + public UserRepresentation createUser(UsersResource users, UserRepresentation user) { + // Add some additional attributes to user + if (CREATE_OBJECTS) { + Map attrs = new HashMap<>(); + attrs.put("attr1", Collections.singletonList("val1")); + attrs.put("attr2", Collections.singletonList("val2")); + user.setAttributes(attrs); + } + + UserRepresentation userRep = super.createUser(users, user); + + // Add password + if (CREATE_OBJECTS) { + CredentialRepresentation password = new CredentialRepresentation(); + password.setType(CredentialRepresentation.PASSWORD); + password.setValue("password"); + password.setTemporary(false); + users.get(userRep.getId()).resetPassword(password); + } + + // Add social link + if (CREATE_SOCIAL_LINKS) { + createSocialLink("facebook", users, userRep.getId()); + } + + return userRep; + } + + private void createSocialLink(String provider, UsersResource users, String userId) { + String uuid = UUID.randomUUID().toString(); + + FederatedIdentityRepresentation link = new FederatedIdentityRepresentation(); + link.setIdentityProvider(provider); + link.setUserId(uuid); + link.setUserName(uuid); + users.get(userId).addFederatedIdentity(provider, link); + } + @Test public void manyUsers() throws IOException { RealmRepresentation realm = realmResource().toRepresentation(); @@ -90,7 +182,19 @@ public class ManyUsersTest extends AbstractUserTest { int i = 0; for (UserRepresentation user : users) { refreshTokenIfMinValidityExpired(); - createUser(realmResource().users(), user); + UserRepresentation createdUser = createUser(realmResource().users(), user); + + // Send additional request to read every user after he is created + if (READ_USER_AFTER_CREATE) { + UserRepresentation returned = realmResource().users().get(createdUser.getId()).toRepresentation(); + Assert.assertEquals(returned.getId(), createdUser.getId()); + } + + // Send additional request to read social links of user + if (CREATE_SOCIAL_LINKS) { + List fedIdentities = realmResource().users().get(createdUser.getId()).getFederatedIdentity(); + } + if (++i % BATCH == 0) { usersTimer.reset(); log.info("Created users: " + i + " / " + users.size()); diff --git a/testsuite/integration-arquillian/tests/other/jpa-performance/src/test/resources/log4j.properties b/testsuite/integration-arquillian/tests/other/jpa-performance/src/test/resources/log4j.properties new file mode 100644 index 0000000000..979584600b --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/jpa-performance/src/test/resources/log4j.properties @@ -0,0 +1,61 @@ +# +# Copyright 2016 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. +# + +log4j.rootLogger=info + +log4j.appender.keycloak=org.apache.log4j.ConsoleAppender +log4j.appender.keycloak.layout=org.apache.log4j.PatternLayout +log4j.appender.keycloak.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] %m%n + +log4j.appender.testsuite=org.apache.log4j.ConsoleAppender +log4j.appender.testsuite.layout=org.apache.log4j.PatternLayout +log4j.appender.testsuite.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p %m%n + +log4j.logger.org.keycloak=off, keycloak + +log4j.logger.org.keycloak.testsuite=debug, testsuite +log4j.additivity.org.keycloak.testsuite=false + +# Enable to view events +# log4j.logger.org.keycloak.events=debug + +# Enable to view loaded SPI and Providers +# log4j.logger.org.keycloak.services.DefaultKeycloakSessionFactory=debug +# log4j.logger.org.keycloak.provider.ProviderManager=debug +# log4j.logger.org.keycloak.provider.FileSystemProviderLoaderFactory=debug + +# Liquibase updates logged with "info" by default. Logging level can be changed by system property "keycloak.liquibase.logging.level" +keycloak.liquibase.logging.level=info +log4j.logger.org.keycloak.connections.jpa.updater.liquibase=${keycloak.liquibase.logging.level} +log4j.logger.org.keycloak.connections.jpa=debug + +# Enable to view database updates +# log4j.logger.org.keycloak.connections.mongo.updater.DefaultMongoUpdaterProvider=debug +# log4j.logger.org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory=debug +# log4j.logger.org.keycloak.migration.MigrationModelManager=debug + +# Enable to view kerberos/spnego logging +# log4j.logger.org.keycloak.broker.kerberos=trace + +# Enable to view detailed AS REQ and TGS REQ requests to embedded Kerberos server +# log4j.logger.org.apache.directory.server.kerberos=debug + +log4j.logger.org.xnio=off +log4j.logger.org.hibernate=off +log4j.logger.org.jboss.resteasy=warn +log4j.logger.org.apache.directory.api=warn +log4j.logger.org.apache.directory.server.core=warn \ No newline at end of file From c52500ce8802968906efe072b83e7f4ae1676f83 Mon Sep 17 00:00:00 2001 From: mposolda Date: Thu, 31 Mar 2016 11:47:36 +0200 Subject: [PATCH 2/2] KEYCLOAK-2610 Add more indexes to database --- .../META-INF/jpa-changelog-1.9.2.xml | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-1.9.2.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.9.2.xml index 141d7cbd6b..4a9ce9a873 100644 --- a/model/jpa/src/main/resources/META-INF/jpa-changelog-1.9.2.xml +++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-1.9.2.xml @@ -24,5 +24,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file