keycloak-aplcache
Changes
testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json 3(+2 -1)
Details
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 141d7cb..4a9ce9a 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 @@
<column name="EMAIL" type="VARCHAR(255)"/>
</createIndex>
+ <!-- Indexes for foreign keys are available by default on some RDBMS (for example MySQL) but not on some others (for example PostgreSQL), so explicitly creating them here -->
+ <createIndex indexName="IDX_USER_ROLE_MAPPING" tableName="USER_ROLE_MAPPING">
+ <column name="USER_ID" type="VARCHAR(36)"/>
+ </createIndex>
+ <createIndex indexName="IDX_USER_GROUP_MAPPING" tableName="USER_GROUP_MEMBERSHIP">
+ <column name="USER_ID" type="VARCHAR(36)"/>
+ </createIndex>
+ <createIndex indexName="IDX_USER_CONSENT" tableName="USER_CONSENT">
+ <column name="USER_ID" type="VARCHAR(36)"/>
+ </createIndex>
+ <createIndex indexName="IDX_CONSENT_PROTMAPPER" tableName="USER_CONSENT_PROT_MAPPER">
+ <column name="USER_CONSENT_ID" type="VARCHAR(36)"/>
+ </createIndex>
+ <createIndex indexName="IDX_CONSENT_ROLE" tableName="USER_CONSENT_ROLE">
+ <column name="USER_CONSENT_ID" type="VARCHAR(36)"/>
+ </createIndex>
+
+ <createIndex indexName="IDX_USER_ATTRIBUTE" tableName="USER_ATTRIBUTE">
+ <column name="USER_ID" type="VARCHAR(36)"/>
+ </createIndex>
+ <createIndex indexName="IDX_USER_CREDENTIAL" tableName="CREDENTIAL">
+ <column name="USER_ID" type="VARCHAR(36)"/>
+ </createIndex>
+ <createIndex indexName="IDX_USER_REQACTIONS" tableName="USER_REQUIRED_ACTION">
+ <column name="USER_ID" type="VARCHAR(36)"/>
+ </createIndex>
+
+ <createIndex indexName="IDX_FEDIDENTITY_USER" tableName="FEDERATED_IDENTITY">
+ <column name="USER_ID" type="VARCHAR(36)"/>
+ </createIndex>
+ <createIndex indexName="IDX_FEDIDENTITY_FEDUSER" tableName="FEDERATED_IDENTITY">
+ <column name="FEDERATED_USER_ID" type="VARCHAR(255)"/>
+ </createIndex>
+
</changeSet>
</databaseChangeLog>
\ No newline at end of file
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 f5d64a5..9208927 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 238bd4c..9e05577 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 914f748..bbbb9d3 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<String, Object> 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<FederatedIdentityRepresentation> 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 0000000..9795846
--- /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