keycloak-aplcache

KEYCLOAK-5897 Support for multiple provisioning options

11/14/2017 4:58:55 PM

Changes

testsuite/performance/docker-compose-cluster.yml 100(+0 -100)

testsuite/performance/healthcheck.sh 34(+0 -34)

testsuite/performance/load-dump.sh 26(+0 -26)

testsuite/performance/log-tool.sh 5(+0 -5)

testsuite/performance/prepare-dump.sh 18(+0 -18)

Details

diff --git a/testsuite/performance/docker-compose-db-failover.yml b/testsuite/performance/docker-compose-db-failover.yml
index 23b0b71..81e7231 100644
--- a/testsuite/performance/docker-compose-db-failover.yml
+++ b/testsuite/performance/docker-compose-db-failover.yml
@@ -71,7 +71,7 @@ services:
 
 
     keycloak:
-        build: keycloak
+        build: keycloak/target/docker
         image: keycloak_test_keycloak:${KEYCLOAK_VERSION:-latest}
         networks:
             - keycloak
diff --git a/testsuite/performance/keycloak/configure.xml b/testsuite/performance/keycloak/configure.xml
new file mode 100644
index 0000000..b6572ff
--- /dev/null
+++ b/testsuite/performance/keycloak/configure.xml
@@ -0,0 +1,79 @@
+<project name="keycloak-server-configuration" basedir="." >
+
+    <target name="check-if-performance-configured">
+        <available property="performance.configured" file="${project.build.directory}/performance-configured"/>
+        <echo>performance.configured: ${performance.configured}</echo>
+    </target>                            
+    
+    <target name="keycloak-performance-configuration" unless="performance.configured" depends="check-if-performance-configured">
+        <echo>keycloak-performance-configuration</echo>
+        <chmod perm="ug+x">
+            <fileset dir="${server.unpacked.home}/bin">
+                <include name="*.sh"/>
+            </fileset>
+        </chmod>
+        <filter token="MODULE_NAME" value="${jdbc.driver.groupId}"/>
+        <filter token="RESOURCE_ROOT_PATH" value="${jdbc.driver.artifactId}-${jdbc.driver.version}.jar"/>
+        <copy file="${resources.dir}/module.xml"
+              todir="${server.unpacked.home}/modules/system/layers/base/${jdbc.driver.module.path}/main"
+              filtering="true"
+        />
+        <copy todir="${server.unpacked.home}/bin" >
+            <fileset dir="${scripts.dir}/jboss-cli"/>
+        </copy>
+        <exec executable="./${jboss.cli.script}" dir="${server.unpacked.home}/bin" failonerror="true">
+            <arg value="--file=set-keycloak-ds.cli"/>
+        </exec>
+        <exec executable="./${jboss.cli.script}" dir="${server.unpacked.home}/bin" failonerror="true">
+            <arg value="--file=io-worker-threads.cli"/>
+        </exec>
+        <exec executable="./${jboss.cli.script}" dir="${server.unpacked.home}/bin" failonerror="true">
+            <arg value="--file=undertow.cli"/>
+        </exec>
+        <exec executable="./${jboss.cli.script}" dir="${server.unpacked.home}/bin" failonerror="true">
+            <arg value="--file=modcluster-simple-load-provider.cli"/>
+        </exec>
+        <exec executable="./${jboss.cli.script}" dir="${server.unpacked.home}/bin" failonerror="true">
+            <arg value="--file=io-worker-threads.cli"/>
+        </exec>
+        <exec executable="./${add.user.script}" dir="${server.unpacked.home}/bin" failonerror="true">
+            <arg value="-u"/>
+            <arg value="${keycloak.debug.user}"/>
+            <arg value="-p"/>
+            <arg value="${keycloak.debug.user.password}"/>
+        </exec>
+        <delete dir="${server.unpacked.home}/standalone/configuration/standalone_xml_history"/>
+        <touch file="${project.build.directory}/performance-configured"/>
+    </target>
+
+        
+    <target name="check-if-crossdc-configured">
+        <available property="crossdc.configured" file="${project.build.directory}/crossdc-configured"/>
+        <echo>crossdc.configured: ${crossdc.configured}</echo>
+    </target>         
+    
+    <target name="keycloak-crossdc-configuration" unless="crossdc.configured" depends="check-if-crossdc-configured">
+        <echo>keycloak-crossdc-configuration</echo>
+        <exec executable="./${jboss.cli.script}" dir="${server.unpacked.home}/bin" failonerror="true">
+            <arg value="--file=add-remote-cache-stores.cli"/>
+        </exec>
+        <delete dir="${server.unpacked.home}/standalone/configuration/standalone_xml_history"/>
+        <touch file="${project.build.directory}/crossdc-configured"/>
+    </target>
+
+
+    <target name="keycloak-docker">
+        <copy todir="${project.build.directory}/docker" overwrite="false">
+            <fileset dir="${scripts.dir}">
+                <include name="Dockerfile"/>
+                <include name="*.sh"/>
+            </fileset>
+        </copy>
+        <copy todir="${project.build.directory}/docker/keycloak" overwrite="false">
+            <fileset dir="${server.unpacked.home}">
+                <exclude name="bin/*.cli"/>
+            </fileset>
+        </copy>
+    </target>
+
+</project>
diff --git a/testsuite/performance/keycloak/pom.xml b/testsuite/performance/keycloak/pom.xml
index 7c6ecb0..8e520bb 100644
--- a/testsuite/performance/keycloak/pom.xml
+++ b/testsuite/performance/keycloak/pom.xml
@@ -37,24 +37,43 @@
     <properties>
         <server.groupId>org.keycloak</server.groupId>
         <server.artifactId>keycloak-server-dist</server.artifactId>
-        <server.unpacked.folder>keycloak-${product.version}</server.unpacked.folder>
+        <server.unpacked.home>${project.build.directory}/keycloak-${product.version}</server.unpacked.home>
+        
+        <jdbc.driver.groupId>org.mariadb.jdbc</jdbc.driver.groupId>
+        <jdbc.driver.artifactId>mariadb-java-client</jdbc.driver.artifactId>
+        <jdbc.driver.version>2.0.3</jdbc.driver.version>
+        <jdbc.driver.module.path>org/mariadb/jdbc</jdbc.driver.module.path>
+
+        <script.extension>sh</script.extension>
+        <jboss.cli.script>jboss-cli.${script.extension}</jboss.cli.script>
+        <add.user.script>add-user.${script.extension}</add.user.script>
+        
+        <skip.crossdc.configuration>true</skip.crossdc.configuration>
+        
+        <skip.configuration>false</skip.configuration>
+        <skip.keycloak.docker>false</skip.keycloak.docker>
+        
+        <keycloak.debug.user>admin</keycloak.debug.user>
+        <keycloak.debug.user.password>admin</keycloak.debug.user.password>
+        
+        <scripts.dir>${project.build.scriptSourceDirectory}</scripts.dir>
+        <resources.dir>${project.basedir}/src/main/resources</resources.dir>
     </properties>
 
     <build>
-
+        
         <plugins>
             <plugin>
                 <artifactId>maven-dependency-plugin</artifactId>
                 <executions>
                     <execution>
-                        <!--=============================-->
                         <id>unpack-keycloak-server-dist</id>
-                        <!--=============================-->
                         <phase>generate-resources</phase>
                         <goals>
                             <goal>unpack</goal>
                         </goals>
                         <configuration>
+                            <overWriteIfNewer>true</overWriteIfNewer>
                             <artifactItems>
                                 <artifactItem>
                                     <groupId>${server.groupId}</groupId>
@@ -66,6 +85,25 @@
                             </artifactItems>
                         </configuration>
                     </execution>
+                    <execution>
+                        <id>copy-jdbc-driver</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>copy</goal>
+                        </goals>
+                        <configuration>
+                            <overWriteIfNewer>true</overWriteIfNewer>
+                            <artifactItems>
+                                <artifactItem>
+                                    <groupId>${jdbc.driver.groupId}</groupId>
+                                    <artifactId>${jdbc.driver.artifactId}</artifactId>
+                                    <version>${jdbc.driver.version}</version>
+                                    <type>jar</type>
+                                    <outputDirectory>${server.unpacked.home}/modules/system/layers/base/${jdbc.driver.module.path}/main</outputDirectory>
+                                </artifactItem>
+                            </artifactItems>
+                        </configuration>
+                    </execution>
                 </executions>
             </plugin>
             <plugin>
@@ -73,18 +111,40 @@
                 <artifactId>maven-antrun-plugin</artifactId>
                 <executions>
                     <execution>
-                        <!--=============================-->
-                        <id>rename-keycloak-folder</id>
-                        <!--=============================-->
+                        <id>keycloak-performance-configuration</id>
                         <phase>process-resources</phase>
                         <goals>
                             <goal>run</goal>
                         </goals>
                         <configuration>
                             <target>
-                                <move todir="${project.build.directory}/keycloak" failonerror="false">
-                                    <fileset dir="${project.build.directory}/${server.unpacked.folder}"/>
-                                </move>
+                                <ant antfile="configure.xml" target="keycloak-performance-configuration" />
+                            </target>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>keycloak-crossdc-configuration</id>
+                        <phase>process-resources</phase>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                        <configuration>
+                            <skip>${skip.crossdc.configuration}</skip>
+                            <target>
+                                <ant antfile="configure.xml" target="keycloak-crossdc-configuration" />
+                            </target>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>keycloak-docker</id>
+                        <phase>process-resources</phase>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                        <configuration>
+                            <skip>${skip.keycloak.docker}</skip>
+                            <target>
+                                <ant antfile="configure.xml" target="keycloak-docker" />
                             </target>
                         </configuration>
                     </execution>
@@ -95,14 +155,34 @@
     
     <profiles>
         
+        <profile>        
+            <id>windows</id>
+            <activation>
+                <os>
+                    <family>windows</family>
+                </os>
+            </activation>
+            <properties>
+                <script.extension>ps1</script.extension>
+            </properties>
+        </profile>
+        
+        <profile>
+            <id>crossdc</id>
+            <properties>
+                <skip.crossdc.configuration>false</skip.crossdc.configuration>
+            </properties>
+        </profile>
+        
         <profile>
             <id>integration-testsuite-server</id>
             <properties>
                 <server.groupId>org.keycloak.testsuite</server.groupId>
                 <server.artifactId>integration-arquillian-servers-auth-server-wildfly</server.artifactId>
-                <server.unpacked.folder>auth-server-wildfly</server.unpacked.folder>
+                <server.unpacked.home>auth-server-wildfly</server.unpacked.home>
             </properties>
         </profile>
+
     </profiles>
     
 </project>
\ No newline at end of file
diff --git a/testsuite/performance/monitoring/cadvisor/Dockerfile b/testsuite/performance/monitoring/cadvisor/Dockerfile
index 56cc630..fa12b15 100644
--- a/testsuite/performance/monitoring/cadvisor/Dockerfile
+++ b/testsuite/performance/monitoring/cadvisor/Dockerfile
@@ -1,4 +1,5 @@
 FROM google/cadvisor:v0.26.1
 RUN apk add --no-cache bash curl
 ADD entrypoint.sh /entrypoint.sh
+RUN chmod +x -v /entrypoint.sh
 ENTRYPOINT ["/entrypoint.sh"]
diff --git a/testsuite/performance/monitoring/grafana/Dockerfile b/testsuite/performance/monitoring/grafana/Dockerfile
index 545e19b..20d2893 100644
--- a/testsuite/performance/monitoring/grafana/Dockerfile
+++ b/testsuite/performance/monitoring/grafana/Dockerfile
@@ -4,4 +4,5 @@ ENV GF_DASHBOARDS_JSON_PATH /etc/grafana/dashboards/
 COPY resource-usage-per-container.json /etc/grafana/dashboards/
 COPY resource-usage-combined.json /etc/grafana/dashboards/
 ADD entrypoint.sh /entrypoint.sh
+RUN chmod +x -v /entrypoint.sh
 ENTRYPOINT ["/entrypoint.sh"]
diff --git a/testsuite/performance/README.docker-compose.md b/testsuite/performance/README.docker-compose.md
index f0256e9..b92e545 100644
--- a/testsuite/performance/README.docker-compose.md
+++ b/testsuite/performance/README.docker-compose.md
@@ -1,35 +1,20 @@
-# Keycloak Performance Testsuite - Docker Compose
+# Keycloak Performance Testsuite - Docker Compose Provisioner
 
-## Requirements:
-- Maven 3.1.1+
-- Unpacked Keycloak server distribution in `keycloak/target` folder.
-- Docker 1.13+
-- Docker Compose 1.14+
+## Supported Deployments
 
-### Keycloak Server Distribution
-To unpack the current Keycloak server distribution into `keycloak/target` folder:
-1. Build and install the distribution by running `mvn install -Pdistribution` from the root of the Keycloak project.
-2. Unpack the installed artifact by running `mvn process-resources` from the Performance Testsuite module.
+| Deployment   | Available Operations                                  | Orchestration Template          |
+| ------------ | ----------------------------------------------------- | ------------------------------- |
+| `singlenode` | `provision`, `teardown`, `export-dump`, `import-dump` | `docker-compose.yml`            |
+| `cluster`    | `provision`, `teardown`, `export-dump`, `import-dump` | `docker-compose-cluster.yml`*   |
+| `crossdc`    | `provision`, `teardown`, `export-dump`, `import-dump` | `docker-compose-crossdc.yml`*   |
+| `monitoring` | `provision`, `teardown`                               | `docker-compose-monitoring.yml` |
 
-## Deployments
+The docker-compose orchestration templates are located in `tests/src/main/docker-compose` directory.
 
-### Singlenode Deployment
-- Build / rebuild: `docker-compose build`
-- Start services: `docker-compose up -d --build`
-  Note: The `--build` parameter triggers a rebuild/restart of changed services if they are already running.
-- Stop services: `docker-compose down -v`. If you wish to keep the container volumes skip the `-v` option.
-
-### Keycloak Cluster Deployment
-- Build / rebuild: `docker-compose -f docker-compose-cluster.yml build`
-- Start services: `docker-compose -f docker-compose-cluster.yml up -d --build`
-- Scaling KC nodes: `docker-compose -f docker-compose-cluster.yml up -d --build --scale keycloak=2`
-- Stop services: `docker-compose -f docker-compose-cluster.yml down -v`. If you wish to keep the container volumes skip the `-v` option.
-
-### Cross-DC Deployment
-- Build / rebuild: `docker-compose -f docker-compose-crossdc.yml build`
-- Start services: `docker-compose -f docker-compose-crossdc.yml up -d --build`
-- Scaling KC nodes: `docker-compose -f docker-compose-crossdc.yml up -d --build --scale keycloak_dc1=2 --scale keycloak_dc2=3`
-- Stop services: `docker-compose -f docker-compose-crossdc.yml down -v`. If you wish to keep the container volumes skip the `-v` option.
+**[*]** The cluster and crossdc templates are generated dynamically during the `provision` operation based on provided `cpusets` parameter.
+One Keycloak service entry is generated for each cpuset. This is a workaround for limitations of the default docker-compose scaling mechanism 
+which only allows setting `cpuset` per service, not per container. For more predictable performance results it is necessary for each 
+Keycloak server to have an exclusive access to specific CPU cores.
 
 ## Debugging docker containers:
 - List started containers: `docker ps`. It's useful to watch continuously: `watch docker ps`.
diff --git a/testsuite/performance/README.log-tool.md b/testsuite/performance/README.log-tool.md
index 07c23cf..25c85b1 100644
--- a/testsuite/performance/README.log-tool.md
+++ b/testsuite/performance/README.log-tool.md
@@ -18,8 +18,8 @@ LOG_DIR=$HOME/devel/keycloak/keycloak/testsuite/performance/tests/target/gatling
 
 Get general statistics about the run to help with deciding about the interval to extract:
 ```
-./log-tool.sh -s -f $LOG_DIR/simulation.log 
-./log-tool.sh -s -f $LOG_DIR/simulation.log --lastRequest "Browser logout"
+tests/log-tool.sh -s -f $LOG_DIR/simulation.log 
+tests/log-tool.sh -s -f $LOG_DIR/simulation.log --lastRequest "Browser logout"
 ```
 
 Set start and end times for the extraction, and create new directory for results:
@@ -34,9 +34,9 @@ mkdir $RESULT_DIR
 
 Extract a portion of the original log, and inspect statistics of resulting log:
 ```
-./log-tool.sh -f $LOG_DIR/simulation.log -o $RESULT_DIR/simulation-$FROM\_$TO.log -e --start $FROM --end $TO 
+tests/log-tool.sh -f $LOG_DIR/simulation.log -o $RESULT_DIR/simulation-$FROM\_$TO.log -e --start $FROM --end $TO 
 
-./log-tool.sh -f $RESULT_DIR/simulation-$FROM\_$TO.log -s
+tests/log-tool.sh -f $RESULT_DIR/simulation-$FROM\_$TO.log -s
 ```
 
 Generate another set of reports from extracted log: 
diff --git a/testsuite/performance/README.md b/testsuite/performance/README.md
index 3eadbcc..6884e4a 100644
--- a/testsuite/performance/README.md
+++ b/testsuite/performance/README.md
@@ -1,11 +1,13 @@
 # Keycloak Performance Testsuite
 
 ## Requirements:
+- Bash 2.05+
 - Maven 3.1.1+
 - Keycloak server distribution installed in the local Maven repository. To do this run `mvn install -Pdistribution` from the root of the Keycloak project.
+
+### Docker Compose Provisioner
 - Docker 1.13+
 - Docker Compose 1.14+
-- Bash
 
 ## Getting started for the impatient
 
@@ -47,30 +49,50 @@ Keep reading for more information.
 
 ## Provisioning
 
+### Available provisioners:
+
+- `docker-compose` **Default.** See [`README.docker-compose.md`](README.docker-compose.md) for more details.
+
 ### Provision
 
-Usage: `mvn verify -Pprovision[,cluster] [-D<PARAM>=<VALUE> ...]`. 
+Usage: `mvn verify -Pprovision [-Dprovisioner=<PROVISIONER>] [-D<PARAMETER>=<VALUE>] …`. 
 
-- Single node deployment: `mvn verify -Pprovision`
-- Cluster deployment: `mvn verify -Pprovision,cluster [-Dkeycloak.scale=N]`. Default `N=1`.
+#### Deployment Types
 
-Available parameters are described in [README.provisioning-parameters](README.provisioning-parameters.md).
+- Single node: `mvn verify -Pprovision`
+- Cluster: `mvn verify -Pprovision,cluster [-Dkeycloak.scale=N] [-Dkeycloak.cpusets="cpuset1 cpuset2 … cpusetM"]`. `N ∈ {1 .. M}`.
+- Cross-DC: `mvn verify -Pprovision,crossdc [-Dkeycloak.dc1.scale=K] [-Dkeycloak.dc2.scale=L] [-Dkeycloak.dc1.cpusets=…] [-Dkeycloak.dc2.cpusets=…]`
 
-### Teardown
+All available parameters are described in [`README.provisioning-parameters.md`](README.provisioning-parameters.md).
+
+#### Provisioned System
 
-Usage: `mvn verify -Pteardown[,cluster]`
+The `provision` operation will produce a `provisioned-system.properties` inside the `tests/target` directory 
+with information about the provisioned system such as the type of deployment and URLs of Keycloak servers and load balancers.
+This information is then used by operations `generate-data`, `import-dump`, `test`, `teardown`.
+
+Provisioning can be run multiple times with different parameters. The system will be updated/reprovisioned based on the new parameters.
+However when switching between different deployment types (e.g. from `singlenode` to `cluster`) it is always necessary 
+to tear down the currently running system.
+
+**Note:** When switching deployment type from `singlenode` or `cluster` to `crossdc` (or the other way around) 
+it is necessary to update the generated Keycloak server configuration (inside `keycloak/target` directory) by 
+adding a `clean` goal to the provisioning command like so: `mvn clean verify -Pprovision …`. It is *not* necessary to update this configuration 
+when switching between `singlenode` and `cluster` deployments.
+
+### Teardown
 
-- Single node deployment: `mvn verify -Pteardown`
-- Cluster deployment: `mvn verify -Pteardown,cluster`
+Usage: `mvn verify -Pteardown [-Dprovisioner=<PROVISIONER>]`
 
-Provisioning/teardown is performed via `docker-compose` tool. More details in [README.docker-compose](README.docker-compose.md).
+**Note:** Unless the provisioned system has been properly torn down the maven build will not allow a cleanup of the `tests/target` directory
+because it contains the `provisioned-system.properties` with information about the still-running system.
 
 
 ## Testing
 
 ### Generate Test Data
 
-Usage: `mvn verify -Pgenerate-data[,cluster] [-Ddataset=DATASET] [-Dexport-dump] [-D<dataset.property>=<value>]`.
+Usage: `mvn verify -Pgenerate-data [-Ddataset=DATASET] [-D<dataset.property>=<value>]`.
 
 Dataset properties are loaded from `datasets/${dataset}.properties` file. Individual properties can be overriden by specifying `-D` params.
 
@@ -97,7 +119,7 @@ Usage: `mvn verify -Pimport-dump [-Ddataset=DATASET]`
 
 ### Run Tests
 
-Usage: `mvn verify -Ptest[,cluster] [-DrunUsers=N] [-DrampUpPeriod=SECONDS] [-DnumOfIterations=N] [-Ddataset=DATASET] [-D<dataset.property>=<value>]* [-D<test.property>=<value>]* `.
+Usage: `mvn verify -Ptest [-DrunUsers=N] [-DrampUpPeriod=SECONDS] [-DnumOfIterations=N] [-Ddataset=DATASET] [-D<dataset.property>=<value>]* [-D<test.property>=<value>]* `.
 
 _*Note:* The same dataset properties which were used for data generation/import should be supplied to the `test` phase._
 
diff --git a/testsuite/performance/README.provisioning-parameters.md b/testsuite/performance/README.provisioning-parameters.md
index eaea691..b291159 100644
--- a/testsuite/performance/README.provisioning-parameters.md
+++ b/testsuite/performance/README.provisioning-parameters.md
@@ -1,48 +1,122 @@
 # Keycloak Performance Testsuite - Provisioning Parameters
 
-## Keycloak Server Settings:
+## Overview of Provisioned Services
 
-| Category    | Setting                       | Property                           | Default value                                                    |
-|-------------|-------------------------------|------------------------------------|------------------------------------------------------------------|
-| JVM         | Memory settings               | `keycloak.jvm.memory`              | -Xms64m -Xmx2g -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m   |
-| Undertow    | HTTP Listener max connections | `keycloak.http.max-connections`    | 500                                                              |
-|             | AJP Listener max connections  | `keycloak.ajp.max-connections`     | 500                                                              |
-| IO          | Worker IO thread pool         | `keycloak.worker.io-threads`       | 2                                                                |
-|             | Worker Task thread pool       | `keycloak.worker.task-max-threads` | 16                                                               |
-| Datasources | Connection pool min size      | `keycloak.ds.min-pool-size`        | 10                                                               |
-|             | Connection pool max size      | `keycloak.ds.max-pool-size`        | 100                                                              |
-|             | Connection pool prefill       | `keycloak.ds.pool-prefill`         | true                                                             |
-|             | Prepared statement cache size | `keycloak.ds.ps-cache-size`        | 100                                                              |
+### Testing
 
-## Load Balancer Settings:
+| Deployment      | Keycloak Server                          | Database           | Load Balancer      | Infinispan Server  |
+|-----------------|------------------------------------------|--------------------|--------------------|--------------------|
+| *Singlenode*    | 1 instance                               | 1 instance         | -                  | -                  |
+| *Cluster*       | N instances                              | 1 instance         | 1 instance         | -                  |
+| *Cross-DC*      | K instances in DC1 + L instances in DC2  | 1 instance per DC  | 1 instance per DC  | 1 instance per DC  |
 
-| Category    | Setting                       | Property                              | Default value                                                    |
-|-------------|-------------------------------|---------------------------------------|------------------------------------------------------------------|
-| JVM         | Memory settings               | `keycloak-lb.jvm.memory`              | -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m |
-| Undertow    | HTTP Listener max connections | `keycloak-lb.http.max-connections`    | 500                                                              |
-| IO          | Worker IO thread pool         | `keycloak-lb.worker.io-threads`       | 2                                                                |
-|             | Worker Task thread pool       | `keycloak-lb.worker.task-max-threads` | 16                                                               |
+### Monitoring
 
-## Infinispan Server Settings
+| Deployment      | CAdvisor    | Influx DB   | Grafana     |
+|-----------------|-------------|-------------|-------------|
+| *Monitoring*    | 1 instance  | 1 instance  | 1 instance  |
 
-| Category    | Setting                       | Property                | Default value                                                                           |
-|-------------|-------------------------------|-------------------------|-----------------------------------------------------------------------------------------|
-| JVM         | Memory settings               | `infinispan.jvm.memory` | -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -XX:+DisableExplicitGC |
 
-## Docker settings
+## Service Parameters
+
+### Keycloak Server
+
+| Category    | Setting                       | Property                           | Default Value                                                      |
+|-------------|-------------------------------|------------------------------------|--------------------------------------------------------------------|
+| Scaling<sup>[1]</sup> | Scale for cluster   | `keycloak.scale`                   | Maximum size<sup>[2]</sup> of cluster.                             |
+|             | Scale for DC1                 | `keycloak.dc1.scale`               | Maximum size of DC1.                                               |
+|             | Scale for DC2                 | `keycloak.dc2.scale`               | Maximum size of DC2.                                               |
+| Docker      | Allocated CPUs                | `keycloak.docker.cpusets`          | `2-3` for singlenode, `2 3` for cluster deployment                 |
+|             | Allocated CPUs for DC1        | `keycloak.dc1.docker.cpusets`      | `2`                                                                |
+|             | Allocated CPUs for DC2        | `keycloak.dc2.docker.cpusets`      | `3`                                                                |
+|             | Available memory              | `keycloak.docker.memlimit`         | `2500m`                                                            |
+| JVM         | Memory settings               | `keycloak.jvm.memory`              | `-Xms64m -Xmx2g -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m`   |
+| Undertow    | HTTP Listener max connections | `keycloak.http.max-connections`    | `50000`                                                            |
+|             | AJP Listener max connections  | `keycloak.ajp.max-connections`     | `50000`                                                            |
+| IO          | Worker IO thread pool         | `keycloak.worker.io-threads`       | `2`                                                                |
+|             | Worker Task thread pool       | `keycloak.worker.task-max-threads` | `16`                                                               |
+| Datasources | Connection pool min size      | `keycloak.ds.min-pool-size`        | `10`                                                               |
+|             | Connection pool max size      | `keycloak.ds.max-pool-size`        | `100`                                                              |
+|             | Connection pool prefill       | `keycloak.ds.pool-prefill`         | `true`                                                             |
+|             | Prepared statement cache size | `keycloak.ds.ps-cache-size`        | `100`                                                              |
+
+**[ 1 ]** The scaling parameters are optional. They can be set within interval from 1 to the maximum cluster size].
+If not set they are automatically set to the maximum size of the cluster (DC1/DC2 respectively).
+
+**[ 2 ]** Maximum cluster size is determined by provisioner-specific parameter such as `keycloak.docker.cpusets` for the default *docker-compose* provisioner.
+The maximum cluster size corresponds to the number of cpusets.
+
+### Database
+
+| Category    | Setting                       | Property                           | Default Value                                                      |
+|-------------|-------------------------------|------------------------------------|--------------------------------------------------------------------|
+| Docker      | Allocated CPUs                | `db.docker.cpusets`                | `1`                                                                |
+|             | Allocated CPUs for DC1        | `db.dc1.docker.cpusets`            | `1`                                                                |
+|             | Allocated CPUs for DC2        | `db.dc2.docker.cpusets`            | `1`                                                                |
+|             | Available memory              | `db.docker.memlimit`               | `2g`                                                               |
+
+### Load Balancer
+
+| Category    | Setting                       | Property                     | Default Value                                                      |
+|-------------|-------------------------------|------------------------------|--------------------------------------------------------------------|
+| Docker      | Allocated CPUs                | `lb.docker.cpusets`          | `1`                                                                |
+|             | Allocated CPUs for DC1        | `lb.dc1.docker.cpusets`      | `1`                                                                |
+|             | Allocated CPUs for DC2        | `lb.dc2.docker.cpusets`      | `1`                                                                |
+|             | Available memory              | `lb.docker.memlimit`         | `1g`                                                               |
+| JVM         | Memory settings               | `lb.jvm.memory`              | `-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m` |
+| Undertow    | HTTP Listener max connections | `lb.http.max-connections`    | `50000`                                                            |
+| IO          | Worker IO thread pool         | `lb.worker.io-threads`       | `2`                                                                |
+|             | Worker Task thread pool       | `lb.worker.task-max-threads` | `16`                                                               |
+
+### Infinispan Server
+
+| Category    | Setting                       | Property                        | Default Value                                                                             |
+|-------------|-------------------------------|---------------------------------|-------------------------------------------------------------------------------------------|
+| Docker      | Allocated CPUs for DC1        | `infinispan.dc1.docker.cpusets` | `1`                                                                                       |
+|             | Allocated CPUs for DC2        | `infinispan.dc2.docker.cpusets` | `1`                                                                                       |
+|             | Available memory              | `infinispan.docker.memlimit`    | `1500m`                                                                                   |
+| JVM         | Memory settings               | `infinispan.jvm.memory`         | `-Xms64m -Xmx1g -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -XX:+DisableExplicitGC`   |
+
+### Monitoring
+
+| Category    | Setting                       | Property                    | Default Value   |
+|-------------|-------------------------------|-----------------------------|-----------------|
+| Docker      | Allocated CPUs                | `monitoring.docker.cpusets` | `0`             |
+
+
+## Note on Docker settings
 
 By default, there are 4 CPU cores allocated: core 0 for monitoring, core 1 for database (MariaDB), and cores 2 and 3 for Keycloak server.
 Default memory limits for database and Keycloak server are 2g. The `cpuset` and `memlimit` parameters set here are set to `cpuset` and
 `mem_limit` parameters of docker-compose configuration. See docker-compose documentation for meaning of the values. How to set the parameters
 correctly depends on number of factors - number of cpu cores, NUMA, available memory etc., hence it is out of scope of this document.
 
-| Container   | Setting                       | Property                        | Default value                                         |
-|-------------|-------------------------------|---------------------------------|-------------------------------------------------------|
-| Keycloak    | Allocated CPUs                | `keycloak.docker.cpuset`        | 2-3                                                   |
-|             | Allocated CPUs for DC1        | `keycloak.dc1.docker.cpuset`    | 2-3                                                   |
-|             | Allocated CPUs for DC2        | `keycloak.dc2.docker.cpuset`    | 2-3                                                   |
-|             | Available memory              | `keycloak.docker.memlimit`      | 2g                                                    |
-| MariaDB     | Allocated CPUs                | `db.docker.cpuset`              | 1                                                     |
-|             | Available memory              | `db.docker.memlimit`            | 2g                                                    |
-| Monitoring  | Allocated CPUs                | `monitoring.docker.cpuset`      | 0                                                     |
+### Example CPU Settings
+
+| HW          | Development Machine  | "Fat Box"    |
+|-------------|----------------------|--------------|
+| Cores       | 4                    | 48           |
+| NUMA Nodes  | 0-3                  | 0-23, 24-47  |
+
+#### Cluster
+
+| Setting                            | Development Machine  | "Fat Box"                   |
+|------------------------------------|----------------------|-----------------------------|
+| `monitoring.docker.cpusets`        | 0                    | 0                           |
+| `db.docker.cpusets`                | 1                    | 1                           |
+| `lb.docker.cpusets`                | 1                    | 2                           |
+| `keycloak.docker.cpusets`          | 2-3                  | 3-6 7-10 11-16 … 43-46      |
+
+#### Cross-DC
 
+| Setting                            | Development Machine  | "Fat Box"                      |
+|------------------------------------|----------------------|--------------------------------|
+| `monitoring.docker.cpusets`        | 0                    | 0                              |
+| `db.dc1.docker.cpusets`            | 1                    | 1                              |
+| `lb.dc1.docker.cpusets`            | 1                    | 2                              |
+| `infinispan.dc1.docker.cpusets`    | 1                    | 3                              |
+| `keycloak.dc1.docker.cpusets`      | 2                    | 4-7 8-11 12-15 16-19 20-23     |
+| `db.dc2.docker.cpusets`            | 1                    | 24                             |
+| `lb.dc2.docker.cpusets`            | 1                    | 25                             |
+| `infinispan.dc2.docker.cpusets`    | 1                    | 26                             |
+| `keycloak.dc2.docker.cpusets`      | 3                    | 27-30 31-34 35-38 39-42 43-46  |
diff --git a/testsuite/performance/tests/common.sh b/testsuite/performance/tests/common.sh
new file mode 100755
index 0000000..b64f46a
--- /dev/null
+++ b/testsuite/performance/tests/common.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+PROJECT_BASEDIR=${PROJECT_BASEDIR:-$(cd "$(dirname "$0")"; pwd)}
+PROJECT_BUILD_DIRECTORY=$PROJECT_BASEDIR/target
+
+export PROVISIONED_SYSTEM_PROPERTIES_FILE="$PROJECT_BUILD_DIRECTORY/provisioned-system.properties"
diff --git a/testsuite/performance/tests/datasets/200ku200c.properties b/testsuite/performance/tests/datasets/200ku200c.properties
new file mode 100644
index 0000000..9228099
--- /dev/null
+++ b/testsuite/performance/tests/datasets/200ku200c.properties
@@ -0,0 +1,8 @@
+numOfRealms=1
+usersPerRealm=200000
+clientsPerRealm=200
+realmRoles=2
+realmRolesPerUser=2
+clientRolesPerUser=2
+clientRolesPerClient=2
+hashIterations=27500
diff --git a/testsuite/performance/tests/datasets/500ku500c.properties b/testsuite/performance/tests/datasets/500ku500c.properties
new file mode 100644
index 0000000..88dc49c
--- /dev/null
+++ b/testsuite/performance/tests/datasets/500ku500c.properties
@@ -0,0 +1,8 @@
+numOfRealms=1
+usersPerRealm=500000
+clientsPerRealm=500
+realmRoles=2
+realmRolesPerUser=2
+clientRolesPerUser=2
+clientRolesPerClient=2
+hashIterations=27500
diff --git a/testsuite/performance/tests/docker-compose.sh b/testsuite/performance/tests/docker-compose.sh
new file mode 100755
index 0000000..9e0aebf
--- /dev/null
+++ b/testsuite/performance/tests/docker-compose.sh
@@ -0,0 +1,371 @@
+#!/bin/bash
+
+### FUNCTIONS ###
+
+function runCommand() {
+    echo "$1"
+    if ! eval "$1" ; then 
+        echo "Execution of command failed."
+        echo "Command: \"$1\""
+        exit 1;
+    fi
+}
+
+function generateDockerComposeFile() {
+    echo "Generating $( basename "$DOCKER_COMPOSE_FILE" )"
+    local TEMPLATES_PATH=$DEPLOYMENT
+    cat $TEMPLATES_PATH/docker-compose-base.yml > $DOCKER_COMPOSE_FILE
+    case "$DEPLOYMENT" in
+        cluster)
+            I=0
+            for CPUSET in $KEYCLOAK_CPUSETS ; do
+                I=$((I+1))
+                sed -e s/%I%/$I/ -e s/%CPUSET%/$CPUSET/ $TEMPLATES_PATH/docker-compose-keycloak.yml >> $DOCKER_COMPOSE_FILE
+            done
+        ;;
+        crossdc) 
+            I=0
+            for CPUSET in $KEYCLOAK_DC1_CPUSETS ; do
+                I=$((I+1))
+                sed -e s/%I%/$I/ -e s/%CPUSET%/$CPUSET/ $TEMPLATES_PATH/docker-compose-keycloak_dc1.yml >> $DOCKER_COMPOSE_FILE
+            done
+            I=0
+            for CPUSET in $KEYCLOAK_DC2_CPUSETS ; do
+                I=$((I+1))
+                sed -e s/%I%/$I/ -e s/%CPUSET%/$CPUSET/ $TEMPLATES_PATH/docker-compose-keycloak_dc2.yml >> $DOCKER_COMPOSE_FILE
+            done
+        ;;
+    esac
+}
+
+function inspectDockerPortMapping() {
+    local PORT="$1"
+    local CONTAINER="$2"
+    local INSPECT_COMMAND="docker inspect --format='{{(index (index .NetworkSettings.Ports \"$PORT\") 0).HostPort}}' $CONTAINER"
+    MAPPED_PORT="$( eval "$INSPECT_COMMAND" )"
+    if [ -z "$MAPPED_PORT" ]; then 
+        echo "Error finding mapped port for $CONTAINER."
+        exit 1
+    fi
+}
+
+function generateProvisionedSystemProperties() {
+    echo "Generating $PROVISIONED_SYSTEM_PROPERTIES_FILE"
+    echo "deployment=$DEPLOYMENT" > $PROVISIONED_SYSTEM_PROPERTIES_FILE
+    echo "keycloak.docker.services=$KEYCLOAK_SERVICES" >> $PROVISIONED_SYSTEM_PROPERTIES_FILE
+    case "$DEPLOYMENT" in
+        singlenode)
+            inspectDockerPortMapping 8080/tcp ${PROJECT_NAME}_keycloak_1
+            echo "keycloak.frontend.servers=http://localhost:$MAPPED_PORT/auth" >> $PROVISIONED_SYSTEM_PROPERTIES_FILE
+        ;;
+        cluster)
+            inspectDockerPortMapping 8080/tcp ${PROJECT_NAME}_loadbalancer_1
+            echo "keycloak.frontend.servers=http://localhost:$MAPPED_PORT/auth" >> $PROVISIONED_SYSTEM_PROPERTIES_FILE
+            BACKEND_URLS=""
+            for SERVICE in $KEYCLOAK_SERVICES ; do
+                inspectDockerPortMapping 8080/tcp ${PROJECT_NAME}_${SERVICE}_1
+                BACKEND_URLS="$BACKEND_URLS http://localhost:$MAPPED_PORT/auth"
+            done
+            echo "keycloak.backend.servers=$BACKEND_URLS" >> $PROVISIONED_SYSTEM_PROPERTIES_FILE
+        ;;
+        crossdc) 
+            inspectDockerPortMapping 8080/tcp ${PROJECT_NAME}_loadbalancer_dc1_1
+            KC_DC1_PORT=$MAPPED_PORT
+            inspectDockerPortMapping 8080/tcp ${PROJECT_NAME}_loadbalancer_dc2_1
+            KC_DC2_PORT=$MAPPED_PORT
+            echo "keycloak.frontend.servers=http://localhost:$KC_DC1_PORT/auth http://localhost:$KC_DC2_PORT/auth" >> $PROVISIONED_SYSTEM_PROPERTIES_FILE
+            BACKEND_URLS=""
+            for SERVICE in $KEYCLOAK_SERVICES ; do
+                inspectDockerPortMapping 8080/tcp ${PROJECT_NAME}_${SERVICE}_1
+                BACKEND_URLS="$BACKEND_URLS http://localhost:$MAPPED_PORT/auth"
+            done
+            echo "keycloak.backend.servers=$BACKEND_URLS" >> $PROVISIONED_SYSTEM_PROPERTIES_FILE
+        ;;
+    esac
+}
+
+function loadProvisionedSystemProperties() {
+    if [ -f $PROVISIONED_SYSTEM_PROPERTIES_FILE ]; then 
+        echo "Loading $PROVISIONED_SYSTEM_PROPERTIES_FILE"
+        export DEPLOYMENT=$( grep -Po "(?<=^deployment=).*" $PROVISIONED_SYSTEM_PROPERTIES_FILE )
+        export KEYCLOAK_SERVICES=$( grep -Po "(?<=^keycloak.docker.services=).*" $PROVISIONED_SYSTEM_PROPERTIES_FILE )
+    else
+        echo "$PROVISIONED_SYSTEM_PROPERTIES_FILE not found."
+    fi
+}
+
+function removeProvisionedSystemProperties() {
+    rm -f $PROVISIONED_SYSTEM_PROPERTIES_FILE
+}
+
+function isTestDeployment() {
+    IS_TEST_DEPLOYMENT=false
+    case "$DEPLOYMENT" in
+        singlenode|cluster|crossdc) IS_TEST_DEPLOYMENT=true ;;
+    esac
+    $IS_TEST_DEPLOYMENT
+}
+
+function validateNotEmpty() {
+    VARIABLE_NAME=$1
+    VARIABLE_VALUE=$2
+#    echo "$VARIABLE_NAME: $VARIABLE_VALUE"
+    if [ -z "$VARIABLE_VALUE" ]; then echo "$VARIABLE_NAME must contain at least one item."; exit 1; fi
+}
+
+
+### SCRIPT ###
+
+cd "$(dirname "$0")"
+. ./common.sh
+
+cd $PROJECT_BUILD_DIRECTORY/docker-compose
+
+PROJECT_NAME=performance
+
+
+export DEPLOYMENT="${DEPLOYMENT:-singlenode}"
+if isTestDeployment ; then loadProvisionedSystemProperties; fi
+export OPERATION="${OPERATION:-provision}"
+
+echo "DEPLOYMENT: $DEPLOYMENT"
+
+case "$DEPLOYMENT" in
+    singlenode) DOCKER_COMPOSE_FILE=docker-compose.yml ;;
+    cluster) DOCKER_COMPOSE_FILE=docker-compose-cluster.yml ;;
+    crossdc) DOCKER_COMPOSE_FILE=docker-compose-crossdc.yml ;;
+    monitoring) DOCKER_COMPOSE_FILE=docker-compose-monitoring.yml ; DELETE_DATA="${DELETE_DATA:-false}" ;;
+    *)
+        echo "Deployment '$DEPLOYMENT' not supported by provisioner '$PROVISIONER'."
+        exit 1
+    ;;
+esac
+
+
+echo "OPERATION: $OPERATION"
+
+case "$OPERATION" in
+
+    provision)
+
+        case "$DEPLOYMENT" in
+
+            singlenode)
+                BASE_SERVICES="mariadb"
+                KEYCLOAK_SERVICES="keycloak"
+
+                validateNotEmpty DB_CPUSETS $DB_CPUSETS
+                DB_CPUSETS_ARRAY=( $DB_CPUSETS )
+                DB_CPUSET=${DB_CPUSETS_ARRAY[0]}
+
+                validateNotEmpty KEYCLOAK_CPUSETS $KEYCLOAK_CPUSETS
+                KEYCLOAK_CPUSETS_ARRAY=( $KEYCLOAK_CPUSETS )
+                KEYCLOAK_CPUSET=${KEYCLOAK_CPUSETS_ARRAY[0]}
+
+                echo "DB_CPUSET: $DB_CPUSET"
+                echo "KEYCLOAK_CPUSET: $KEYCLOAK_CPUSET"
+                echo "BASE_SERVICES: $BASE_SERVICES"
+                echo "KEYCLOAK_SERVICES: $KEYCLOAK_SERVICES"
+
+            ;;
+
+            cluster)
+                BASE_SERVICES="mariadb loadbalancer"
+
+                validateNotEmpty DB_CPUSETS $DB_CPUSETS
+                DB_CPUSETS_ARRAY=( $DB_CPUSETS )
+                DB_CPUSET=${DB_CPUSETS_ARRAY[0]}
+
+                validateNotEmpty LB_CPUSETS $LB_CPUSETS
+                LB_CPUSETS_ARRAY=( $LB_CPUSETS )
+                LB_CPUSET=${LB_CPUSETS_ARRAY[0]}
+
+                validateNotEmpty KEYCLOAK_CPUSETS $KEYCLOAK_CPUSETS
+                KEYCLOAK_CPUSETS_ARRAY=( $KEYCLOAK_CPUSETS )
+
+                KEYCLOAK_MAX_SCALE=${#KEYCLOAK_CPUSETS_ARRAY[@]}
+                KEYCLOAK_SCALE="${KEYCLOAK_SCALE:-$KEYCLOAK_MAX_SCALE}"
+                if [ $KEYCLOAK_SCALE -gt $KEYCLOAK_MAX_SCALE ]; then KEYCLOAK_SCALE=$KEYCLOAK_MAX_SCALE; fi
+                if [ $KEYCLOAK_SCALE -lt 1 ]; then KEYCLOAK_SCALE=1; fi
+
+                echo "DB_CPUSET: $DB_CPUSET"
+                echo "KEYCLOAK_CPUSETS: $KEYCLOAK_CPUSETS"
+                echo "KEYCLOAK_SCALE: ${KEYCLOAK_SCALE} (max ${KEYCLOAK_MAX_SCALE})"
+
+                KEYCLOAK_SERVICES=""
+                STOPPED_KEYCLOAK_SERVICES=""
+                for ((i=1; i<=$KEYCLOAK_MAX_SCALE; i++)) ; do
+                    if (( $i <= $KEYCLOAK_SCALE )) ; then
+                        KEYCLOAK_SERVICES="$KEYCLOAK_SERVICES keycloak_$i"
+                    else
+                        STOPPED_KEYCLOAK_SERVICES="$STOPPED_KEYCLOAK_SERVICES keycloak_$i"
+                    fi
+                done
+                echo "BASE_SERVICES: $BASE_SERVICES"
+                echo "KEYCLOAK_SERVICES: $KEYCLOAK_SERVICES"
+                
+                generateDockerComposeFile
+            ;;
+
+            crossdc)
+                BASE_SERVICES="mariadb_dc1 mariadb_dc2 infinispan_dc1 infinispan_dc2 loadbalancer_dc1 loadbalancer_dc2"
+
+                validateNotEmpty DB_DC1_CPUSETS $DB_DC1_CPUSETS
+                validateNotEmpty DB_DC2_CPUSETS $DB_DC2_CPUSETS
+                DB_DC1_CPUSETS_ARRAY=( $DB_DC1_CPUSETS )
+                DB_DC2_CPUSETS_ARRAY=( $DB_DC2_CPUSETS )
+                DB_DC1_CPUSET=${DB_DC1_CPUSETS_ARRAY[0]}
+                DB_DC2_CPUSET=${DB_DC2_CPUSETS_ARRAY[0]}
+                echo "DB_DC1_CPUSET: $DB_DC1_CPUSET"
+                echo "DB_DC2_CPUSET: $DB_DC2_CPUSET"
+
+                validateNotEmpty LB_DC1_CPUSETS $LB_DC1_CPUSETS
+                validateNotEmpty LB_DC2_CPUSETS $LB_DC2_CPUSETS
+                LB_DC1_CPUSETS_ARRAY=( $LB_DC1_CPUSETS )
+                LB_DC2_CPUSETS_ARRAY=( $LB_DC2_CPUSETS )
+                LB_DC1_CPUSET=${LB_DC1_CPUSETS_ARRAY[0]}
+                LB_DC2_CPUSET=${LB_DC2_CPUSETS_ARRAY[0]}
+                echo "LB_DC1_CPUSET: $LB_DC1_CPUSET"
+                echo "LB_DC2_CPUSET: $LB_DC2_CPUSET"
+
+                validateNotEmpty INFINISPAN_DC1_CPUSETS $INFINISPAN_DC1_CPUSETS
+                validateNotEmpty INFINISPAN_DC2_CPUSETS $INFINISPAN_DC2_CPUSETS
+                INFINISPAN_DC1_CPUSETS_ARRAY=( $INFINISPAN_DC1_CPUSETS )
+                INFINISPAN_DC2_CPUSETS_ARRAY=( $INFINISPAN_DC2_CPUSETS )
+                INFINISPAN_DC1_CPUSET=${INFINISPAN_DC1_CPUSETS_ARRAY[0]}
+                INFINISPAN_DC2_CPUSET=${INFINISPAN_DC2_CPUSETS_ARRAY[0]}
+                echo "INFINISPAN_DC1_CPUSET: $INFINISPAN_DC1_CPUSET"
+                echo "INFINISPAN_DC2_CPUSET: $INFINISPAN_DC2_CPUSET"
+
+                validateNotEmpty KEYCLOAK_DC1_CPUSETS $KEYCLOAK_DC1_CPUSETS
+                validateNotEmpty KEYCLOAK_DC2_CPUSETS $KEYCLOAK_DC2_CPUSETS
+                KEYCLOAK_DC1_CPUSETS_ARRAY=( $KEYCLOAK_DC1_CPUSETS )
+                KEYCLOAK_DC2_CPUSETS_ARRAY=( $KEYCLOAK_DC2_CPUSETS )
+                KEYCLOAK_DC1_MAX_SCALE=${#KEYCLOAK_DC1_CPUSETS_ARRAY[@]}
+                KEYCLOAK_DC2_MAX_SCALE=${#KEYCLOAK_DC2_CPUSETS_ARRAY[@]}
+                KEYCLOAK_DC1_SCALE="${KEYCLOAK_DC1_SCALE:-$KEYCLOAK_DC1_MAX_SCALE}"
+                KEYCLOAK_DC2_SCALE="${KEYCLOAK_DC2_SCALE:-$KEYCLOAK_DC2_MAX_SCALE}"
+
+                if [ $KEYCLOAK_DC1_SCALE -gt $KEYCLOAK_DC1_MAX_SCALE ]; then KEYCLOAK_DC1_SCALE=$KEYCLOAK_DC1_MAX_SCALE; fi
+                if [ $KEYCLOAK_DC1_SCALE -lt 1 ]; then KEYCLOAK_DC1_SCALE=1; fi
+                if [ $KEYCLOAK_DC2_SCALE -gt $KEYCLOAK_DC2_MAX_SCALE ]; then KEYCLOAK_DC2_SCALE=$KEYCLOAK_DC2_MAX_SCALE; fi
+                if [ $KEYCLOAK_DC2_SCALE -lt 1 ]; then KEYCLOAK_DC2_SCALE=1; fi
+
+                echo "KEYCLOAK_DC1_CPUSETS: ${KEYCLOAK_DC1_CPUSETS}"
+                echo "KEYCLOAK_DC2_CPUSETS: ${KEYCLOAK_DC2_CPUSETS}"
+                echo "KEYCLOAK_DC1_SCALE: ${KEYCLOAK_DC1_SCALE} (max ${KEYCLOAK_DC1_MAX_SCALE})"
+                echo "KEYCLOAK_DC2_SCALE: ${KEYCLOAK_DC2_SCALE} (max ${KEYCLOAK_DC2_MAX_SCALE})"
+
+                KEYCLOAK_SERVICES=""
+                STOPPED_KEYCLOAK_SERVICES=""
+                for ((i=1; i<=$KEYCLOAK_DC1_MAX_SCALE; i++)) ; do
+                    if (( $i <= $KEYCLOAK_DC1_SCALE )) ; then
+                        KEYCLOAK_SERVICES="$KEYCLOAK_SERVICES keycloak_dc1_$i"
+                    else
+                        STOPPED_KEYCLOAK_SERVICES="$STOPPED_KEYCLOAK_SERVICES keycloak_dc1_$i"
+                    fi
+                done
+                for ((i=1; i<=$KEYCLOAK_DC2_MAX_SCALE; i++)) ; do
+                    if (( $i <= $KEYCLOAK_DC2_SCALE )) ; then
+                        KEYCLOAK_SERVICES="$KEYCLOAK_SERVICES keycloak_dc2_$i"
+                    else
+                        STOPPED_KEYCLOAK_SERVICES="$STOPPED_KEYCLOAK_SERVICES keycloak_dc2_$i"
+                    fi
+                done
+                echo "BASE_SERVICES: $BASE_SERVICES"
+                echo "KEYCLOAK_SERVICES: $KEYCLOAK_SERVICES"
+
+                generateDockerComposeFile
+
+            ;;
+
+        esac
+
+        runCommand "docker-compose -f $DOCKER_COMPOSE_FILE -p ${PROJECT_NAME} up -d --build $BASE_SERVICES $KEYCLOAK_SERVICES"
+        if [ ! -z "$STOPPED_KEYCLOAK_SERVICES" ] ; then 
+            echo "STOPPED_KEYCLOAK_SERVICES: $STOPPED_KEYCLOAK_SERVICES"
+            runCommand "docker-compose -f $DOCKER_COMPOSE_FILE -p ${PROJECT_NAME} stop $STOPPED_KEYCLOAK_SERVICES"
+        fi
+
+        if isTestDeployment ; then 
+            generateProvisionedSystemProperties; 
+            $PROJECT_BASEDIR/healthcheck.sh
+        fi
+
+    ;;
+
+
+    teardown)
+
+        DELETE_DATA="${DELETE_DATA:-true}"
+        echo "DELETE_DATA: $DELETE_DATA"
+        if "$DELETE_DATA" ; then VOLUMES_ARG="-v"; else VOLUMES_ARG=""; fi
+
+        runCommand "docker-compose -f $DOCKER_COMPOSE_FILE -p ${PROJECT_NAME} down $VOLUMES_ARG"
+
+        if isTestDeployment ; then removeProvisionedSystemProperties; fi
+    ;;
+
+
+    export-dump|import-dump)
+        
+        loadProvisionedSystemProperties
+
+        echo "KEYCLOAK_SERVICES: $KEYCLOAK_SERVICES"
+        if [ -z "$KEYCLOAK_SERVICES" ]; then echo "Unable to load KEYCLOAK_SERVICES"; exit 1; fi
+
+        case "$DEPLOYMENT" in
+            singlenode|cluster) export DB_CONTAINER=${PROJECT_NAME}_mariadb_1 ;;
+            crossdc) export DB_CONTAINER=${PROJECT_NAME}_mariadb_dc1_1 ;;
+            *) echo "Deployment '$DEPLOYMENT' doesn't support operation '$OPERATION'." ; exit 1 ;;
+        esac
+        if [ -z "$DATASET" ]; then echo "Operation '$OPERATION' requires DATASET parameter."; exit 1; fi
+        echo "DATASET: $DATASET"
+
+        echo "Stopping Keycloak services."
+        runCommand "docker-compose -f $DOCKER_COMPOSE_FILE -p ${PROJECT_NAME} stop $KEYCLOAK_SERVICES"
+
+        cd $PROJECT_BASEDIR/datasets
+        case "$OPERATION" in
+            export-dump)
+                echo "Exporting $DATASET.sql."
+                if docker exec $DB_CONTAINER /usr/bin/mysqldump -u root --password=root keycloak > $DATASET.sql ; then 
+                    echo "Compressing $DATASET.sql."
+                    gzip $DATASET.sql
+                fi
+            ;;
+            import-dump) 
+                DUMP_DOWNLOAD_SITE=${DUMP_DOWNLOAD_SITE:-https://downloads.jboss.org/keycloak-qe}
+                if [ ! -f "$DATASET.sql.gz" ]; then 
+                    echo "Downloading dump file."
+                    if ! curl -f -O $DUMP_DOWNLOAD_SITE/$DATASET.properties -O $DUMP_DOWNLOAD_SITE/$DATASET.sql.gz ; then
+                        echo Download failed.
+                        exit 1
+                    fi
+                fi
+                echo "Importing $DATASET.sql.gz"
+                set -o pipefail
+                if ! zcat $DATASET.sql.gz | docker exec -i $DB_CONTAINER /usr/bin/mysql -u root --password=root keycloak ; then
+                    echo Import failed.
+                    exit 1
+                fi
+            ;;
+        esac
+        cd $PROJECT_BUILD_DIRECTORY/docker-compose
+
+        echo "Starting Keycloak services."
+        runCommand "docker-compose -f $DOCKER_COMPOSE_FILE -p ${PROJECT_NAME} up -d --no-recreate $KEYCLOAK_SERVICES"
+        # need to update mapped ports
+        generateProvisionedSystemProperties
+
+        $PROJECT_BASEDIR/healthcheck.sh
+
+    ;;
+
+    *)
+        echo "Unsupported operation: '$OPERATION'"
+        exit 1
+    ;;
+
+esac
+
diff --git a/testsuite/performance/tests/healthcheck.sh b/testsuite/performance/tests/healthcheck.sh
new file mode 100755
index 0000000..cf14634
--- /dev/null
+++ b/testsuite/performance/tests/healthcheck.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+cd "$(dirname "$0")"
+. ./common.sh
+
+CHECK_TIMEOUT=${CHECK_TIMEOUT:-5}
+function isKeycloakServerReady {
+    CODE=`curl --connect-timeout $CHECK_TIMEOUT -m $CHECK_TIMEOUT -s -o /dev/null -w "%{http_code}" $1/realms/master`
+    [ "$CODE" -eq "200" ]
+}
+
+
+if [ -f "$PROVISIONED_SYSTEM_PROPERTIES_FILE" ] ; then 
+
+    FRONTEND_SERVERS=$( grep -Po "(?<=^keycloak.frontend.servers=).*" "$PROVISIONED_SYSTEM_PROPERTIES_FILE" )
+    BACKEND_SERVERS=$( grep -Po "(?<=^keycloak.backend.servers=).*" "$PROVISIONED_SYSTEM_PROPERTIES_FILE" )
+    KEYCLOAK_SERVERS="$FRONTEND_SERVERS $BACKEND_SERVERS"
+
+    HEALTHCHECK_ITERATIONS=${HEALTHCHECK_ITERATIONS:-20}
+    HEALTHCHECK_WAIT=${HEALTHCHECK_WAIT:-6s}
+    
+    echo "Waiting for Keycloak servers to be ready."
+    echo "Check intervals: $HEALTHCHECK_ITERATIONS x $HEALTHCHECK_WAIT."
+
+    C=0
+    READY=false
+    while ! $READY; do 
+        C=$((C+1))
+        if [ $C -gt $HEALTHCHECK_ITERATIONS ]; then
+            echo System healthcheck failed.
+            exit 1
+        fi
+        echo $( date -Iseconds )
+        READY=true
+        for SERVER in $KEYCLOAK_SERVERS ; do
+            if isKeycloakServerReady $SERVER; then 
+                echo -e "Keycloak server: $SERVER\tREADY"
+            else
+                echo -e "Keycloak server: $SERVER\tNOT READY"
+                READY=false
+            fi
+        done
+        if ! $READY; then sleep $HEALTHCHECK_WAIT ; fi
+    done
+    echo All servers ready.
+
+else
+    echo Healthcheck skipped.
+fi
diff --git a/testsuite/performance/tests/log-tool.sh b/testsuite/performance/tests/log-tool.sh
new file mode 100755
index 0000000..c36cfe2
--- /dev/null
+++ b/testsuite/performance/tests/log-tool.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+cd "$(dirname "$0")"
+. ./common.sh
+
+java -cp $PROJECT_BUILD_DIRECTORY/classes org.keycloak.performance.log.LogProcessor "$@"
\ No newline at end of file
diff --git a/testsuite/performance/tests/pom.xml b/testsuite/performance/tests/pom.xml
index fa5cef8..970a149 100644
--- a/testsuite/performance/tests/pom.xml
+++ b/testsuite/performance/tests/pom.xml
@@ -29,19 +29,23 @@
     <artifactId>performance-tests</artifactId>
     <name>Keycloak Performance TestSuite - Tests</name>
 
-    <description>
-    </description>
-
     <properties>
-        <compose.file>docker-compose.yml</compose.file>
-        <compose.up.params/>
-        <compose.restart.params>keycloak</compose.restart.params>
-        <keycloak.server.uris>http://localhost:8080/auth</keycloak.server.uris>
-        <db.url>jdbc:mariadb://keycloak:keycloak@localhost:3306/keycloak</db.url>
-
+        <provisioner>docker-compose</provisioner>
+        <deployment>singlenode</deployment>
+        
+        <provisioned.system.properties.file>${project.build.directory}/provisioned-system.properties</provisioned.system.properties.file>
+
+        <!-- Keycloak Server Settings -->
+        <keycloak.scale/>
+        <keycloak.dc1.scale/>
+        <keycloak.dc2.scale/>
+        <keycloak.docker.cpusets>2-3</keycloak.docker.cpusets>
+        <keycloak.dc1.docker.cpusets>2</keycloak.dc1.docker.cpusets>
+        <keycloak.dc2.docker.cpusets>3</keycloak.dc2.docker.cpusets>
+        <keycloak.docker.memlimit>2500m</keycloak.docker.memlimit>
         <keycloak.jvm.memory>-Xms64m -Xmx2g -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m</keycloak.jvm.memory>
-        <keycloak.http.max-connections>500</keycloak.http.max-connections>
-        <keycloak.ajp.max-connections>500</keycloak.ajp.max-connections>
+        <keycloak.http.max-connections>50000</keycloak.http.max-connections>
+        <keycloak.ajp.max-connections>50000</keycloak.ajp.max-connections>
         <keycloak.worker.io-threads>2</keycloak.worker.io-threads>
         <keycloak.worker.task-max-threads>16</keycloak.worker.task-max-threads>
         <keycloak.ds.min-pool-size>10</keycloak.ds.min-pool-size>
@@ -49,24 +53,32 @@
         <keycloak.ds.pool-prefill>true</keycloak.ds.pool-prefill>
         <keycloak.ds.ps-cache-size>100</keycloak.ds.ps-cache-size>
 
-        <keycloak-lb.jvm.memory>-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m</keycloak-lb.jvm.memory>
-        <keycloak-lb.http.max-connections>500</keycloak-lb.http.max-connections>
-        <keycloak-lb.worker.io-threads>2</keycloak-lb.worker.io-threads>
-        <keycloak-lb.worker.task-max-threads>16</keycloak-lb.worker.task-max-threads>
-
-        <!-- Docker-related properties -->
-        <db.docker.cpuset>1</db.docker.cpuset>
-        <keycloak.docker.cpuset>2-3</keycloak.docker.cpuset>
-        <keycloak.dc1.docker.cpuset>2</keycloak.dc1.docker.cpuset>
-        <keycloak.dc2.docker.cpuset>3</keycloak.dc2.docker.cpuset>
-        <monitoring.docker.cpuset>0</monitoring.docker.cpuset>
-
+        <!-- Database Settings -->
+        <db.docker.cpusets>1</db.docker.cpusets>
+        <db.dc1.docker.cpusets>1</db.dc1.docker.cpusets>
+        <db.dc2.docker.cpusets>1</db.dc2.docker.cpusets>
         <db.docker.memlimit>2g</db.docker.memlimit>
-        <keycloak.docker.memlimit>2g</keycloak.docker.memlimit>
-        <!-- End of docker-related properties -->
 
-        <infinispan.jvm.memory>-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -XX:+DisableExplicitGC</infinispan.jvm.memory>
+        <!-- Load Balancer Settings -->
+        <lb.docker.cpusets>1</lb.docker.cpusets>
+        <lb.dc1.docker.cpusets>1</lb.dc1.docker.cpusets>
+        <lb.dc2.docker.cpusets>1</lb.dc2.docker.cpusets>
+        <lb.docker.memlimit>1g</lb.docker.memlimit>
+        <lb.jvm.memory>-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m</lb.jvm.memory>
+        <lb.http.max-connections>500</lb.http.max-connections>
+        <lb.worker.io-threads>2</lb.worker.io-threads>
+        <lb.worker.task-max-threads>16</lb.worker.task-max-threads>
+
+        <!-- Infinispan Settings -->
+        <infinispan.dc1.docker.cpusets>1</infinispan.dc1.docker.cpusets>
+        <infinispan.dc2.docker.cpusets>1</infinispan.dc2.docker.cpusets>
+        <infinispan.docker.memlimit>1500m</infinispan.docker.memlimit>
+        <infinispan.jvm.memory>-Xms64m -Xmx1g -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -XX:+DisableExplicitGC</infinispan.jvm.memory>
+        
+        <!-- Monitoring Settings -->
+        <monitoring.docker.cpusets>0</monitoring.docker.cpusets>
 
+        <!-- Other -->
         <dataset>default</dataset>
         <numOfWorkers>1</numOfWorkers>
 
@@ -156,6 +168,58 @@
             </testResource>
         </testResources>
         <plugins>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>enforce-teardown-before-clean</id>
+                        <goals>
+                            <goal>enforce</goal>
+                        </goals>
+                        <phase>pre-clean</phase>
+                        <configuration>
+                            <rules>
+                                <requireFilesDontExist>
+                                    <message><![CDATA[
+                                        WARNING: A previously provisioned system still appears to be running.
+         Please tear it down with `mvn verify -P teardown [-Pcluster|crossdc]` before runing `mvn clean`, 
+         or delete the `provisioned-system.properties` file manually.
+                                    ]]></message>
+                                    <files>
+                                        <file>${provisioned.system.properties.file}</file>
+                                    </files>
+                                </requireFilesDontExist>
+                            </rules>
+                            <fail>true</fail>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            
+            
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>properties-maven-plugin</artifactId>
+                <version>1.0.0</version>
+                <executions>            
+                    <execution>
+                        <id>read-existing-provisioned-system-properties</id>
+                        <phase>initialize</phase>
+                        <goals>
+                            <goal>read-project-properties</goal>
+                        </goals>
+                        <configuration>
+                            <files>
+                                <file>${project.build.directory}/provisioned-system.properties</file>
+                            </files>
+                            <quiet>true</quiet>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            
             <plugin>
                 <groupId>net.alchim31.maven</groupId>
                 <artifactId>scala-maven-plugin</artifactId>
@@ -191,12 +255,11 @@
                 </executions>
             </plugin>
 
-
             <plugin>
                 <!--
                 Execute test directly by using:
 
-                    mvn gatling:execute -f testsuite/performance/gatling -Dgatling.simulationClass=keycloak.DemoSimulation2
+                mvn gatling:execute -Ptest -f testsuite/performance/gatling -Dgatling.simulationClass=keycloak.DemoSimulation2
 
                 For more usage info see: http://gatling.io/docs/current/extensions/maven_plugin/
                 -->
@@ -213,6 +276,7 @@
                         <include>keycloak.DemoSimulation2</include>
                     </includes-->
                     <jvmArgs>
+                        <param>-Dkeycloak.server.uris=${keycloak.frontend.servers}</param>
                         <param>-DnumOfRealms=${numOfRealms}</param>
                         <param>-DusersPerRealm=${usersPerRealm}</param>
                         <param>-DclientsPerRealm=${clientsPerRealm}</param>
@@ -233,59 +297,59 @@
                     </execution>
                 </executions>
             </plugin>
-            
+
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
-                <artifactId>properties-maven-plugin</artifactId>
-                <version>1.0.0</version>
-                <executions>
-                    <execution>
-                        <id>read-dataset-properties</id>
-                        <phase>initialize</phase>
-                        <goals>
-                            <goal>read-project-properties</goal>
-                        </goals>
-                        <configuration>
-                            <files>
-                                <file>${project.basedir}/datasets/${dataset}.properties</file>
-                            </files>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>                    
+                <artifactId>exec-maven-plugin</artifactId>
+                <configuration>
+                    <workingDirectory>${project.basedir}</workingDirectory>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
 
     <profiles>
+        
         <profile>
-            <id>initialize-dataset-properties</id>
+            <id>docker-compose</id>
             <activation>
                 <property>
-                    <name>dataset</name>
+                    <name>!provisioner</name>
                 </property>
             </activation>
             <build>
                 <plugins>
                     <plugin>
-                        <groupId>org.codehaus.mojo</groupId>
-                        <artifactId>properties-maven-plugin</artifactId>
-                        <version>1.0.0</version>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-antrun-plugin</artifactId>
                         <executions>
                             <execution>
-                                <id>initialize-dataset-properties</id>
-                                <phase>initialize</phase>
+                                <id>copy-dockerfiles-etc</id>
+                                <phase>generate-resources</phase>
                                 <goals>
-                                    <goal>read-project-properties</goal>
+                                    <goal>run</goal>
                                 </goals>
                                 <configuration>
-                                    <files>
-                                        <file>${project.basedir}/datasets/${dataset}.properties</file>
-                                    </files>
+                                    <target>
+                                        <copy todir="${project.build.directory}/docker-compose" overwrite="false" >
+                                            <fileset dir="${project.basedir}/src/main/docker-compose"/>
+                                        </copy>
+                                        <copy todir="${project.build.directory}/docker-compose" overwrite="false" >
+                                            <fileset dir="${project.basedir}/..">
+                                                <include name="db/**"/>
+                                                <include name="infinispan/**"/>
+                                                <include name="load-balancer/**"/>
+                                                <include name="monitoring/**"/>
+                                            </fileset>
+                                        </copy>
+                                        <copy todir="${project.build.directory}/docker-compose/keycloak" overwrite="false" >
+                                            <fileset dir="${project.basedir}/../keycloak/target/docker"/>
+                                        </copy>
+                                    </target>
                                 </configuration>
                             </execution>
                         </executions>
-                    </plugin>                    
-
+                    </plugin>
                 </plugins>
             </build>
         </profile>
@@ -293,25 +357,17 @@
         <profile>
             <id>cluster</id>
             <properties>
-                <compose.file>docker-compose-cluster.yml</compose.file>
-                <keycloak.scale>1</keycloak.scale>
-                <compose.up.params>--scale keycloak=${keycloak.scale}</compose.up.params>
-                <keycloak.server.uris>http://localhost:8080/auth</keycloak.server.uris>
+                <deployment>cluster</deployment>
+                <keycloak.docker.cpusets>2 3</keycloak.docker.cpusets>
             </properties>
         </profile>
         <profile>
             <id>crossdc</id>
             <properties>
-                <compose.file>docker-compose-crossdc.yml</compose.file>
-                <keycloak.dc1.scale>1</keycloak.dc1.scale>
-                <keycloak.dc2.scale>1</keycloak.dc2.scale>
-                <compose.up.params>--scale keycloak_dc1=${keycloak.dc1.scale} --scale keycloak_dc2=${keycloak.dc2.scale}</compose.up.params>
-                <compose.restart.params>keycloak_dc1 keycloak_dc2</compose.restart.params>
-                <keycloak.server.uris>http://localhost:8081/auth http://localhost:8082/auth</keycloak.server.uris>
+                <deployment>crossdc</deployment>
             </properties>
         </profile>
         
-        
         <profile>
             <id>provision</id>
             <build>
@@ -321,25 +377,27 @@
                         <artifactId>exec-maven-plugin</artifactId>
                         <executions>
                             <execution>
-                                <id>docker-compose-up</id>
-                                <phase>pre-integration-test</phase>
+                                <id>provision</id>
+                                <phase>process-test-resources</phase>
                                 <goals>
                                     <goal>exec</goal>
                                 </goals>
                                 <configuration>
-                                    <workingDirectory>${project.basedir}/..</workingDirectory>
-                                    <executable>docker-compose</executable>
-                                    <commandlineArgs>-f ${compose.file} up -d --build ${compose.up.params}</commandlineArgs>
+                                    <executable>./${provisioner}.sh</executable>
                                     <environmentVariables>
-                                        <KEYCLOAK_VERSION>${project.version}</KEYCLOAK_VERSION>
+                                        <PROVISIONER>${provisioner}</PROVISIONER>
+                                        <DEPLOYMENT>${deployment}</DEPLOYMENT>
+                                        <OPERATION>provision</OPERATION>
 
-                                        <KEYCLOAK_CPUSET>${keycloak.docker.cpuset}</KEYCLOAK_CPUSET>
-                                        <KEYCLOAK_DC1_CPUSET>${keycloak.dc1.docker.cpuset}</KEYCLOAK_DC1_CPUSET>
-                                        <KEYCLOAK_DC2_CPUSET>${keycloak.dc2.docker.cpuset}</KEYCLOAK_DC2_CPUSET>
-                                        <KEYCLOAK_MEMLIMIT>${keycloak.docker.memlimit}</KEYCLOAK_MEMLIMIT>
-                                        <DB_CPUSET>${db.docker.cpuset}</DB_CPUSET>
-                                        <DB_MEMLIMIT>${db.docker.memlimit}</DB_MEMLIMIT>
+                                        <KEYCLOAK_VERSION>${project.version}</KEYCLOAK_VERSION>
                                         
+                                        <KEYCLOAK_SCALE>${keycloak.scale}</KEYCLOAK_SCALE>
+                                        <KEYCLOAK_DC1_SCALE>${keycloak.dc1.scale}</KEYCLOAK_DC1_SCALE>
+                                        <KEYCLOAK_DC2_SCALE>${keycloak.dc2.scale}</KEYCLOAK_DC2_SCALE>
+                                        <KEYCLOAK_CPUSETS>${keycloak.docker.cpusets}</KEYCLOAK_CPUSETS>
+                                        <KEYCLOAK_DC1_CPUSETS>${keycloak.dc1.docker.cpusets}</KEYCLOAK_DC1_CPUSETS>
+                                        <KEYCLOAK_DC2_CPUSETS>${keycloak.dc2.docker.cpusets}</KEYCLOAK_DC2_CPUSETS>
+                                        <KEYCLOAK_MEMLIMIT>${keycloak.docker.memlimit}</KEYCLOAK_MEMLIMIT>
                                         <KEYCLOAK_JVM_MEMORY>${keycloak.jvm.memory}</KEYCLOAK_JVM_MEMORY>
                                         <KEYCLOAK_HTTP_MAX_CONNECTIONS>${keycloak.http.max-connections}</KEYCLOAK_HTTP_MAX_CONNECTIONS>
                                         <KEYCLOAK_AJP_MAX_CONNECTIONS>${keycloak.ajp.max-connections}</KEYCLOAK_AJP_MAX_CONNECTIONS>
@@ -349,51 +407,82 @@
                                         <KEYCLOAK_DS_MAX_POOL_SIZE>${keycloak.ds.max-pool-size}</KEYCLOAK_DS_MAX_POOL_SIZE>
                                         <KEYCLOAK_DS_POOL_PREFILL>${keycloak.ds.pool-prefill}</KEYCLOAK_DS_POOL_PREFILL>
                                         <KEYCLOAK_DS_PS_CACHE_SIZE>${keycloak.ds.ps-cache-size}</KEYCLOAK_DS_PS_CACHE_SIZE>
+
+                                        <DB_CPUSETS>${db.docker.cpusets}</DB_CPUSETS>
+                                        <DB_DC1_CPUSETS>${db.dc1.docker.cpusets}</DB_DC1_CPUSETS>
+                                        <DB_DC2_CPUSETS>${db.dc2.docker.cpusets}</DB_DC2_CPUSETS>
+                                        <DB_MEMLIMIT>${db.docker.memlimit}</DB_MEMLIMIT>
+
+                                        <LB_CPUSETS>${lb.docker.cpusets}</LB_CPUSETS>
+                                        <LB_DC1_CPUSETS>${lb.dc1.docker.cpusets}</LB_DC1_CPUSETS>
+                                        <LB_DC2_CPUSETS>${lb.dc2.docker.cpusets}</LB_DC2_CPUSETS>
+                                        <LB_MEMLIMIT>${lb.docker.memlimit}</LB_MEMLIMIT>
+                                        <LB_JVM_MEMORY>${lb.jvm.memory}</LB_JVM_MEMORY>
+                                        <LB_HTTP_MAX_CONNECTIONS>${lb.http.max-connections}</LB_HTTP_MAX_CONNECTIONS>
+                                        <LB_WORKER_IO_THREADS>${lb.worker.io-threads}</LB_WORKER_IO_THREADS>
+                                        <LB_WORKER_TASK_MAX_THREADS>${lb.worker.task-max-threads}</LB_WORKER_TASK_MAX_THREADS>
                                         
-                                        <KEYCLOAK_LB_JVM_MEMORY>${keycloak-lb.jvm.memory}</KEYCLOAK_LB_JVM_MEMORY>
-                                        <KEYCLOAK_LB_HTTP_MAX_CONNECTIONS>${keycloak-lb.http.max-connections}</KEYCLOAK_LB_HTTP_MAX_CONNECTIONS>
-                                        <KEYCLOAK_LB_WORKER_IO_THREADS>${keycloak-lb.worker.io-threads}</KEYCLOAK_LB_WORKER_IO_THREADS>
-                                        <KEYCLOAK_LB_WORKER_TASK_MAX_THREADS>${keycloak-lb.worker.task-max-threads}</KEYCLOAK_LB_WORKER_TASK_MAX_THREADS>
-                                        
+                                        <INFINISPAN_DC1_CPUSETS>${infinispan.dc1.docker.cpusets}</INFINISPAN_DC1_CPUSETS>
+                                        <INFINISPAN_DC2_CPUSETS>${infinispan.dc2.docker.cpusets}</INFINISPAN_DC2_CPUSETS>
+                                        <INFINISPAN_MEMLIMIT>${infinispan.docker.memlimit}</INFINISPAN_MEMLIMIT>
                                         <INFINISPAN_JVM_MEMORY>${infinispan.jvm.memory}</INFINISPAN_JVM_MEMORY>
                                     </environmentVariables>
                                 </configuration>
                             </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>properties-maven-plugin</artifactId>
+                        <version>1.0.0</version>
+                        <executions>
                             <execution>
-                                <id>healthcheck</id>
-                                <phase>pre-integration-test</phase>
+                                <id>read-new-provisioned-system-properties</id>
                                 <goals>
-                                    <goal>exec</goal>
+                                    <goal>read-project-properties</goal>
                                 </goals>
+                                <phase>pre-integration-test</phase>
                                 <configuration>
-                                    <executable>./healthcheck.sh</executable>
-                                    <workingDirectory>${project.basedir}/..</workingDirectory>
-                                    <environmentVariables>
-                                        <KEYCLOAK_SERVER_URIS>${keycloak.server.uris}</KEYCLOAK_SERVER_URIS>
-                                    </environmentVariables>
+                                    <files>
+                                        <file>${project.build.directory}/provisioned-system.properties</file>
+                                    </files>
                                 </configuration>
                             </execution>
                         </executions>
                     </plugin>
+                </plugins>
+            </build>
+        </profile>
+        
+        <profile>
+            <id>initialize-dataset-properties</id>
+            <activation>
+                <property>
+                    <name>dataset</name>
+                </property>
+            </activation>
+            <build>
+                <plugins>
                     <plugin>
-                        <artifactId>maven-antrun-plugin</artifactId>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>properties-maven-plugin</artifactId>
+                        <version>1.0.0</version>
                         <executions>
                             <execution>
-                                <id>write-provisioned-system-properties</id>
+                                <id>initialize-dataset-properties</id>
                                 <phase>pre-integration-test</phase>
                                 <goals>
-                                    <goal>run</goal>
+                                    <goal>read-project-properties</goal>
                                 </goals>
                                 <configuration>
-                                    <target>
-                                        <propertyfile file="${project.build.directory}/provisioned-system.properties">
-                                            <entry key="keycloak.server.uris" value="${keycloak.server.uris}"/>
-                                        </propertyfile>
-                                    </target>
+                                    <files>
+                                        <file>${project.basedir}/../datasets/${dataset}.properties</file>
+                                    </files>
+                                    <quiet>true</quiet>
                                 </configuration>
                             </execution>
                         </executions>
-                    </plugin>
+                    </plugin>                    
                 </plugins>
             </build>
         </profile>
@@ -442,6 +531,7 @@
                                     <arguments>
                                         <argument>-classpath</argument>
                                         <classpath/>
+                                        <argument>-Dkeycloak.server.uris=${keycloak.frontend.servers}</argument>
                                         <argument>-DnumOfWorkers=${numOfWorkers}</argument>
                                         <argument>org.keycloak.performance.RealmsConfigurationLoader</argument>
                                         <argument>benchmark-realms.json</argument>
@@ -450,48 +540,34 @@
                             </execution>
                         </executions>
                     </plugin>
+                </plugins>
+            </build>
+        </profile>
+
+        <profile>
+            <id>export-dump</id>
+            <build>
+                <plugins>
                     <plugin>
-                        <artifactId>maven-antrun-plugin</artifactId>
+                        <artifactId>maven-enforcer-plugin</artifactId>
                         <executions>
                             <execution>
-                                <id>write-imported-dataset-properties</id>
-                                <phase>pre-integration-test</phase>
+                                <id>enforce-nondefault-dataset</id>
                                 <goals>
-                                    <goal>run</goal>
+                                    <goal>enforce</goal>
                                 </goals>
                                 <configuration>
-                                    <target>
-                                        <propertyfile file="${project.build.directory}/imported-dataset.properties">
-                                            <entry key="numOfRealms" value="${numOfRealms}"/>
-                                            <entry key="usersPerRealm" value="${usersPerRealm}"/>
-                                            <entry key="clientsPerRealm" value="${clientsPerRealm}"/>
-                                            <entry key="realmRoles" value="${realmRoles}"/>
-                                            <entry key="realmRolesPerUser" value="${realmRolesPerUser}"/>
-                                            <entry key="clientRolesPerUser" value="${clientRolesPerUser}"/>
-                                            <entry key="clientRolesPerClient" value="${clientRolesPerClient}"/>
-                                            <entry key="hashIterations" value="${hashIterations}"/>
-                                        </propertyfile>
-                                    </target>
+                                    <rules>
+                                        <requireProperty>
+                                            <property>dataset</property>
+                                            <regex>(?!default).*</regex>
+                                            <regexMessage>For the "export-dump" task property "dataset" cannot be set to "default".</regexMessage>
+                                        </requireProperty>
+                                    </rules>
                                 </configuration>
                             </execution>
                         </executions>
                     </plugin>
-                </plugins>
-            </build>
-        </profile>
-
-        <profile>
-            <id>export-dump-after-generation</id>
-
-            <activation>
-                <activeByDefault>false</activeByDefault>
-                <property>
-                    <name>export-dump</name>
-                </property>
-            </activation>
-
-            <build>
-                <plugins>
                     <plugin>
                         <groupId>org.codehaus.mojo</groupId>
                         <artifactId>exec-maven-plugin</artifactId>
@@ -503,10 +579,11 @@
                                     <goal>exec</goal>
                                 </goals>
                                 <configuration>
-                                    <workingDirectory>${project.basedir}/..</workingDirectory>
-                                    <executable>./prepare-dump.sh</executable>
-
+                                    <executable>./${provisioner}.sh</executable>
                                     <environmentVariables>
+                                        <PROVISIONER>${provisioner}</PROVISIONER>
+                                        <DEPLOYMENT>${deployment}</DEPLOYMENT>
+                                        <OPERATION>export-dump</OPERATION>
                                         <DATASET>${dataset}</DATASET>
                                     </environmentVariables>
                                 </configuration>
@@ -526,46 +603,21 @@
                         <artifactId>exec-maven-plugin</artifactId>
                         <executions>
                             <execution>
-                                <id>load-dump</id>
+                                <id>import-dump</id>
                                 <phase>pre-integration-test</phase>
                                 <goals>
                                     <goal>exec</goal>
                                 </goals>
                                 <configuration>
-                                    <workingDirectory>${project.basedir}/..</workingDirectory>
-                                    <executable>./load-dump.sh</executable>
-
+                                    <executable>./${provisioner}.sh</executable>
                                     <environmentVariables>
+                                        <PROVISIONER>${provisioner}</PROVISIONER>
+                                        <DEPLOYMENT>${deployment}</DEPLOYMENT>
+                                        <OPERATION>import-dump</OPERATION>
                                         <DATASET>${dataset}</DATASET>
                                     </environmentVariables>
                                 </configuration>
                             </execution>
-                            <execution>
-                                <id>restart-keycloak</id>
-                                <phase>pre-integration-test</phase>
-                                <goals>
-                                    <goal>exec</goal>
-                                </goals>
-                                <configuration>
-                                    <workingDirectory>${project.basedir}/..</workingDirectory>
-                                    <executable>docker-compose</executable>
-                                    <commandlineArgs>-f ${compose.file} restart ${compose.restart.params}</commandlineArgs>
-                                </configuration>
-                            </execution>
-                            <execution>
-                                <id>healthcheck</id>
-                                <phase>pre-integration-test</phase>
-                                <goals>
-                                    <goal>exec</goal>
-                                </goals>
-                                <configuration>
-                                    <executable>./healthcheck.sh</executable>
-                                    <workingDirectory>${project.basedir}/..</workingDirectory>
-                                    <environmentVariables>
-                                        <KEYCLOAK_SERVER_URIS>${keycloak.server.uris}</KEYCLOAK_SERVER_URIS>
-                                    </environmentVariables>
-                                </configuration>
-                            </execution>
                         </executions>
                     </plugin>
                 </plugins>
@@ -582,7 +634,7 @@
         <profile>
             <id>teardown</id>
             <properties>
-                <volumes.arg>-v</volumes.arg>
+                <delete.data>true</delete.data>
             </properties>
             <build>
                 <plugins>
@@ -591,29 +643,30 @@
                         <artifactId>exec-maven-plugin</artifactId>
                         <executions>
                             <execution>
-                                <id>docker-compose-down</id>
+                                <id>teardown</id>
                                 <phase>post-integration-test</phase>
                                 <goals>
                                     <goal>exec</goal>
                                 </goals>
                                 <configuration>
-                                    <workingDirectory>${project.basedir}/..</workingDirectory>
-                                    <executable>docker-compose</executable>
-                                    <commandlineArgs>-f ${compose.file} down ${volumes.arg}</commandlineArgs>
+                                    <executable>./${provisioner}.sh</executable>
                                     <environmentVariables>
-                                        <KEYCLOAK_VERSION>${project.version}</KEYCLOAK_VERSION>
+                                        <PROVISIONER>${provisioner}</PROVISIONER>
+                                        <DEPLOYMENT>${deployment}</DEPLOYMENT>
+                                        <OPERATION>teardown</OPERATION>
+                                        <DELETE_DATA>${delete.data}</DELETE_DATA>
                                     </environmentVariables>
                                 </configuration>
                             </execution>
                         </executions>
-                    </plugin>
+                    </plugin>                    
                 </plugins>
             </build>
         </profile>
         <profile>
             <id>keep-data</id>
             <properties>
-                <volumes.arg/>
+                <delete.data>false</delete.data>
             </properties>
         </profile>
         
@@ -627,29 +680,29 @@
                         <artifactId>exec-maven-plugin</artifactId>
                         <executions>
                             <execution>
-                                <id>monitoring-docker-compose-up</id>
+                                <id>monitoring-on</id>
                                 <phase>pre-integration-test</phase>
                                 <goals>
                                     <goal>exec</goal>
                                 </goals>
                                 <configuration>
-                                    <workingDirectory>${project.basedir}/..</workingDirectory>
-                                    <executable>docker-compose</executable>
-                                    <commandlineArgs>-f docker-compose-monitoring.yml up -d --build</commandlineArgs>
+                                    <executable>./${provisioner}.sh</executable>
                                     <environmentVariables>
-                                        <MONITORING_CPUSET>${monitoring.docker.cpuset}</MONITORING_CPUSET>
+                                        <PROVISIONER>${provisioner}</PROVISIONER>
+                                        <DEPLOYMENT>monitoring</DEPLOYMENT>
+                                        <OPERATION>provision</OPERATION>
                                     </environmentVariables>
                                 </configuration>
                             </execution>
                         </executions>
-                    </plugin>
+                    </plugin> 
                 </plugins>
             </build>
         </profile>
         <profile>
             <id>monitoring-off</id>
             <properties>
-                <monitoring.volumes.arg/>
+                <delete.monitoring.data>false</delete.monitoring.data>
             </properties>
             <build>
                 <plugins>
@@ -658,15 +711,19 @@
                         <artifactId>exec-maven-plugin</artifactId>
                         <executions>
                             <execution>
-                                <id>monitoring-docker-compose-down</id>
+                                <id>monitoring-off</id>
                                 <phase>post-integration-test</phase>
                                 <goals>
                                     <goal>exec</goal>
                                 </goals>
                                 <configuration>
-                                    <workingDirectory>${project.basedir}/..</workingDirectory>
-                                    <executable>docker-compose</executable>
-                                    <commandlineArgs>-f docker-compose-monitoring.yml down ${monitoring.volumes.arg}</commandlineArgs>
+                                    <executable>./${provisioner}.sh</executable>
+                                    <environmentVariables>
+                                        <PROVISIONER>${provisioner}</PROVISIONER>
+                                        <DEPLOYMENT>monitoring</DEPLOYMENT>
+                                        <OPERATION>teardown</OPERATION>
+                                        <DELETE_DATA>${delete.monitoring.data}</DELETE_DATA>
+                                    </environmentVariables>
                                 </configuration>
                             </execution>
                         </executions>
@@ -677,7 +734,7 @@
         <profile>
             <id>delete-monitoring-data</id>
             <properties>
-                <monitoring.volumes.arg>-v</monitoring.volumes.arg>
+                <delete.monitoring.data>true</delete.monitoring.data>
             </properties>
         </profile>
 
diff --git a/testsuite/performance/tests/src/main/docker-compose/cluster/docker-compose-base.yml b/testsuite/performance/tests/src/main/docker-compose/cluster/docker-compose-base.yml
new file mode 100644
index 0000000..7d152c7
--- /dev/null
+++ b/testsuite/performance/tests/src/main/docker-compose/cluster/docker-compose-base.yml
@@ -0,0 +1,51 @@
+version: "2.2"
+
+networks:
+    keycloak:
+        ipam:
+            config:
+            - subnet: 10.0.1.0/24
+#    loadbalancing:
+#        ipam:
+#            config:
+#            - subnet: 10.0.2.0/24
+
+services:
+    
+    mariadb:
+        build: db/mariadb
+        image: keycloak_test_mariadb:${KEYCLOAK_VERSION:-latest}
+        cpuset: ${DB_CPUSET:-1}
+        mem_limit: ${DB_MEMLIMIT:-1g}
+        networks:
+            - keycloak
+        environment:
+            MYSQL_ROOT_PASSWORD: root
+            MYSQL_DATABASE: keycloak
+            MYSQL_USER: keycloak
+            MYSQL_PASSWORD: keycloak
+        ports:
+            - "3306:3306"
+
+    loadbalancer:
+        build: load-balancer/wildfly-modcluster
+        image: keycloak_test_loadbalancer:${KEYCLOAK_VERSION:-latest}
+#        depends_on:
+#            keycloak:
+#                condition: service_healthy
+        cpuset: ${LB_CPUSET:-1}
+        mem_limit: ${LB_MEMLIMIT:-1g}
+        networks:
+            - keycloak
+#            - loadbalancing
+        environment:
+            PRIVATE_SUBNET: 10.0.1.0/24
+#            PUBLIC_SUBNET: 10.0.2.0/24
+            JAVA_OPTS: ${LB_JVM_MEMORY:--Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m} -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true
+            HTTP_MAX_CONNECTIONS: ${LB_HTTP_MAX_CONNECTIONS:-500}
+            WORKER_IO_THREADS: ${LB_WORKER_IO_THREADS:-2}
+            WORKER_TASK_MAX_THREADS: ${LB_WORKER_TASK_MAX_THREADS:-16}
+        ports:
+            - "8080:8080"
+
+
diff --git a/testsuite/performance/tests/src/main/docker-compose/cluster/docker-compose-keycloak.yml b/testsuite/performance/tests/src/main/docker-compose/cluster/docker-compose-keycloak.yml
new file mode 100644
index 0000000..fdc549c
--- /dev/null
+++ b/testsuite/performance/tests/src/main/docker-compose/cluster/docker-compose-keycloak.yml
@@ -0,0 +1,34 @@
+    keycloak_%I%:
+        build: keycloak
+        image: keycloak_test_keycloak:${KEYCLOAK_VERSION:-latest}
+        depends_on:
+            mariadb:
+                condition: service_healthy
+        cpuset: "%CPUSET%"
+        mem_limit: ${KEYCLOAK_MEMLIMIT:-2500m}
+        networks:
+            - keycloak
+        environment:
+            CONFIGURATION: standalone-ha.xml
+            PUBLIC_SUBNET: 10.0.1.0/24
+            PRIVATE_SUBNET: 10.0.1.0/24
+            MARIADB_HOSTS: mariadb:3306
+            MARIADB_DATABASE: keycloak
+            MARIADB_USER: keycloak
+            MARIADB_PASSWORD: keycloak
+            KEYCLOAK_USER: admin
+            KEYCLOAK_PASSWORD: admin
+
+            JAVA_OPTS: ${KEYCLOAK_JVM_MEMORY:--Xms64m -Xmx2g -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m} -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true
+            HTTP_MAX_CONNECTIONS: ${KEYCLOAK_HTTP_MAX_CONNECTIONS:-50000}
+            AJP_MAX_CONNECTIONS: ${KEYCLOAK_AJP_MAX_CONNECTIONS:-50000}
+            WORKER_IO_THREADS: ${KEYCLOAK_WORKER_IO_THREADS:-2}
+            WORKER_TASK_MAX_THREADS: ${KEYCLOAK_WORKER_TASK_MAX_THREADS:-16}
+            DS_MIN_POOL_SIZE: ${KEYCLOAK_DS_MIN_POOL_SIZE:-10}
+            DS_MAX_POOL_SIZE: ${KEYCLOAK_DS_MAX_POOL_SIZE:-100}
+            DS_POOL_PREFILL: "${KEYCLOAK_DS_POOL_PREFILL:-true}"
+            DS_PS_CACHE_SIZE: ${KEYCLOAK_DS_PS_CACHE_SIZE:-100}
+        ports:
+            - "8080"
+            - "9990"
+
diff --git a/testsuite/performance/tests/src/main/docker-compose/crossdc/docker-compose-keycloak_dc1.yml b/testsuite/performance/tests/src/main/docker-compose/crossdc/docker-compose-keycloak_dc1.yml
new file mode 100644
index 0000000..cd72df1
--- /dev/null
+++ b/testsuite/performance/tests/src/main/docker-compose/crossdc/docker-compose-keycloak_dc1.yml
@@ -0,0 +1,40 @@
+    keycloak_dc1_%I%:
+        build: ./keycloak
+        image: keycloak_test_keycloak_dc1:${KEYCLOAK_VERSION:-latest}
+        depends_on:
+            # wait for the db cluster to be ready before starting keycloak
+            mariadb_dc2:
+                condition: service_healthy
+            # wait for the ispn cluster to be ready before starting keycloak
+            infinispan_dc2:
+                condition: service_healthy
+        cpuset: "%CPUSET%"
+        mem_limit: ${KEYCLOAK_MEMLIMIT:-2500m}
+        networks:
+            - dc1_keycloak
+        environment:
+            CONFIGURATION: standalone-ha.xml
+            PUBLIC_SUBNET: 10.1.1.0/24
+            PRIVATE_SUBNET: 10.1.1.0/24
+            MARIADB_HOSTS: mariadb_dc1:3306
+            MARIADB_DATABASE: keycloak
+            MARIADB_USER: keycloak
+            MARIADB_PASSWORD: keycloak
+            KEYCLOAK_USER: admin
+            KEYCLOAK_PASSWORD: admin
+            INFINISPAN_HOST: infinispan_dc1
+            SITE: dc1
+            
+            JAVA_OPTS: ${KEYCLOAK_JVM_MEMORY:--Xms64m -Xmx2g -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m} -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true
+            HTTP_MAX_CONNECTIONS: ${KEYCLOAK_HTTP_MAX_CONNECTIONS:-50000}
+            AJP_MAX_CONNECTIONS: ${KEYCLOAK_AJP_MAX_CONNECTIONS:-50000}
+            WORKER_IO_THREADS: ${KEYCLOAK_WORKER_IO_THREADS:-2}
+            WORKER_TASK_MAX_THREADS: ${KEYCLOAK_WORKER_TASK_MAX_THREADS:-16}
+            DS_MIN_POOL_SIZE: ${KEYCLOAK_DS_MIN_POOL_SIZE:-10}
+            DS_MAX_POOL_SIZE: ${KEYCLOAK_DS_MAX_POOL_SIZE:-100}
+            DS_POOL_PREFILL: "${KEYCLOAK_DS_POOL_PREFILL:-true}"
+            DS_PS_CACHE_SIZE: ${KEYCLOAK_DS_PS_CACHE_SIZE:-100}
+        ports:
+            - "8080"
+            - "9990"
+
diff --git a/testsuite/performance/tests/src/main/docker-compose/crossdc/docker-compose-keycloak_dc2.yml b/testsuite/performance/tests/src/main/docker-compose/crossdc/docker-compose-keycloak_dc2.yml
new file mode 100644
index 0000000..b8fadf2
--- /dev/null
+++ b/testsuite/performance/tests/src/main/docker-compose/crossdc/docker-compose-keycloak_dc2.yml
@@ -0,0 +1,35 @@
+    keycloak_dc2_%I%:
+        build: ./keycloak
+        image: keycloak_test_keycloak_dc2:${KEYCLOAK_VERSION:-latest}
+        depends_on:
+            # wait for first kc instance to be ready before starting another
+            keycloak_dc1_1:
+                condition: service_healthy
+        cpuset: "%CPUSET%"
+        mem_limit: ${KEYCLOAK_MEMLIMIT:-2500m}
+        networks:
+            - dc2_keycloak
+        environment:
+            CONFIGURATION: standalone-ha.xml
+            PUBLIC_SUBNET: 10.2.1.0/24
+            PRIVATE_SUBNET: 10.2.1.0/24
+            MARIADB_HOSTS: mariadb_dc2:3306
+            MARIADB_DATABASE: keycloak
+            MARIADB_USER: keycloak
+            MARIADB_PASSWORD: keycloak
+            INFINISPAN_HOST: infinispan_dc2
+            SITE: dc2
+
+            JAVA_OPTS: ${KEYCLOAK_JVM_MEMORY:--Xms64m -Xmx2g -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m} -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true
+            HTTP_MAX_CONNECTIONS: ${KEYCLOAK_HTTP_MAX_CONNECTIONS:-50000}
+            AJP_MAX_CONNECTIONS: ${KEYCLOAK_AJP_MAX_CONNECTIONS:-50000}
+            WORKER_IO_THREADS: ${KEYCLOAK_WORKER_IO_THREADS:-2}
+            WORKER_TASK_MAX_THREADS: ${KEYCLOAK_WORKER_TASK_MAX_THREADS:-16}
+            DS_MIN_POOL_SIZE: ${KEYCLOAK_DS_MIN_POOL_SIZE:-10}
+            DS_MAX_POOL_SIZE: ${KEYCLOAK_DS_MAX_POOL_SIZE:-100}
+            DS_POOL_PREFILL: "${KEYCLOAK_DS_POOL_PREFILL:-true}"
+            DS_PS_CACHE_SIZE: ${KEYCLOAK_DS_PS_CACHE_SIZE:-100}
+        ports:
+            - "8080"
+            - "9990"
+
diff --git a/testsuite/performance/tests/src/main/java/org/keycloak/performance/RealmsConfigurationLoader.java b/testsuite/performance/tests/src/main/java/org/keycloak/performance/RealmsConfigurationLoader.java
index 764f435..52d1fde 100644
--- a/testsuite/performance/tests/src/main/java/org/keycloak/performance/RealmsConfigurationLoader.java
+++ b/testsuite/performance/tests/src/main/java/org/keycloak/performance/RealmsConfigurationLoader.java
@@ -58,6 +58,7 @@ public class RealmsConfigurationLoader {
     static boolean realmCreated;
 
     public static void main(String [] args) throws IOException {
+        System.out.println("Keycloak servers: "+TestConfig.serverUrisList);
 
         if (args.length == 0) {
             args = new String[] {EXPORT_FILENAME};
diff --git a/testsuite/performance/tests/src/main/java/org/keycloak/performance/TestConfig.java b/testsuite/performance/tests/src/main/java/org/keycloak/performance/TestConfig.java
index 8a28ea1..e95ca87 100644
--- a/testsuite/performance/tests/src/main/java/org/keycloak/performance/TestConfig.java
+++ b/testsuite/performance/tests/src/main/java/org/keycloak/performance/TestConfig.java
@@ -4,6 +4,7 @@ import org.keycloak.performance.util.FilteredIterator;
 import org.keycloak.performance.util.LoopingIterator;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
@@ -77,21 +78,17 @@ public class TestConfig {
 
     static {
         // if KEYCLOAK_SERVER_URIS env var is set, and system property serverUris is not set
-        String servers = System.getProperty("serverUris");
-        if (servers == null) {
-            String env = System.getenv("KEYCLOAK_SERVER_URIS");
-            serverUris = env != null ? env : "http://localhost:8080/auth";
+        String serversProp = System.getProperty("keycloak.server.uris");
+        if (serversProp == null) {
+            String serversEnv = System.getenv("KEYCLOAK_SERVERS");
+            serverUris = serversEnv != null ? serversEnv : "http://localhost:8080/auth";
         } else {
-            serverUris = servers;
+            serverUris = serversProp;
         }
 
         // initialize serverUrisList and serverUrisIterator
-        ArrayList<String> uris = new ArrayList<>();
-        for (String uri: serverUris.split(" ")) {
-            uris.add(uri);
-        }
-        serverUrisList = uris;
-        serverUrisIterator = new LoopingIterator<>(uris);
+        serverUrisList = Arrays.asList(serverUris.split(" "));
+        serverUrisIterator = new LoopingIterator<>(serverUrisList);
     }
 
     // Users iterators by realm
diff --git a/testsuite/performance/tests/src/test/scala/keycloak/AdminConsoleSimulation.scala b/testsuite/performance/tests/src/test/scala/keycloak/AdminConsoleSimulation.scala
index e5465ca..97e323f 100644
--- a/testsuite/performance/tests/src/test/scala/keycloak/AdminConsoleSimulation.scala
+++ b/testsuite/performance/tests/src/test/scala/keycloak/AdminConsoleSimulation.scala
@@ -14,7 +14,7 @@ import SimulationsHelper._
 class AdminConsoleSimulation extends Simulation {
 
   println()
-  println("Using server: " + TestConfig.serverUrisList.get(0))
+  println("Target server: " + TestConfig.serverUrisList.get(0))
   println()
   println("Using test parameters:")
   println("  runUsers: " + TestConfig.runUsers)
diff --git a/testsuite/performance/tests/src/test/scala/keycloak/DefaultSimulation.scala b/testsuite/performance/tests/src/test/scala/keycloak/DefaultSimulation.scala
index 86dd29a..a2ed6d4 100644
--- a/testsuite/performance/tests/src/test/scala/keycloak/DefaultSimulation.scala
+++ b/testsuite/performance/tests/src/test/scala/keycloak/DefaultSimulation.scala
@@ -25,6 +25,9 @@ class DefaultSimulation extends Simulation {
 
 
 
+  println()
+  println("Taget servers: " + TestConfig.serverUrisList)
+  println()
   println("Using test parameters:")
   println("  runUsers: " + TestConfig.runUsers)
   println("  numOfIterations: " + TestConfig.numOfIterations)