You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by jb...@apache.org on 2021/05/17 14:33:32 UTC

[geode-benchmarks] branch develop updated: Adds Redis benchmarks. (#150)

This is an automated email from the ASF dual-hosted git repository.

jbarrett pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode-benchmarks.git


The following commit(s) were added to refs/heads/develop by this push:
     new 3a58d10  Adds Redis benchmarks. (#150)
3a58d10 is described below

commit 3a58d1017b2a53f0ee85c6232f5e6a2d0604b63b
Author: Jacob Barrett <jb...@pivotal.io>
AuthorDate: Mon May 17 07:33:23 2021 -0700

    Adds Redis benchmarks. (#150)
    
    * Adds framework for Redis based benchmarks.
    * Supports benchmarking Redis, Geode and external Redis like systems.
    * Supports Jedis and Lettuce clients.
    * Tunable cluster sizing.
    
    Co-authored-by: Dan Smith <da...@vmware.com>
---
 README.md                                          | 141 +++++++++++++++----
 build.gradle                                       |   7 +-
 geode-benchmarks/build.gradle                      |  61 ++++++---
 .../benchmark/parameters/GcImplementation.java     |   2 +-
 .../benchmark/parameters/GcLoggingParameters.java  |  36 ++---
 .../geode/benchmark/parameters/GcParameters.java   |  10 ++
 .../benchmark/parameters/GeodeProperties.java      |  47 ++++---
 .../geode/benchmark/parameters/JavaVersion.java    |   4 +-
 .../geode/benchmark/parameters/JvmParameters.java  |   8 ++
 ...meters.java => SafepointLoggingParameters.java} |  29 ++--
 .../parameters/GeodeParameters.java}               |  27 ++--
 .../parameters/NettyParameters.java}               |  26 ++--
 .../benchmark/redis/tasks/AbstractPrePopulate.java |  80 +++++++++++
 .../benchmark/redis/tasks/CreateRedisCluster.java  |  90 ++++++++++++
 .../tasks/FlushDbTask.java}                        |  16 ++-
 .../geode/benchmark/redis/tasks/GetRedisTask.java  |  69 ++++++++++
 .../geode/benchmark/redis/tasks/HgetRedisTask.java |  73 ++++++++++
 .../geode/benchmark/redis/tasks/HsetRedisTask.java |  67 +++++++++
 .../redis/tasks/InitRedisServersAttribute.java     |  67 +++++++++
 .../tasks/InitRegion.java}                         |  20 +--
 .../benchmark/redis/tasks/JedisClientManager.java  | 125 +++++++++++++++++
 .../redis/tasks/LettuceClientManager.java          | 126 +++++++++++++++++
 .../benchmark/redis/tasks/LongStringCache.java}    |  28 ++--
 .../tasks/PrePopulateRedis.java}                   |  26 ++--
 .../tasks/PrePopulateRedisHash.java}               |  28 ++--
 .../tasks/RedisClient.java}                        |  14 +-
 .../tasks/RedisClientManager.java}                 |  18 ++-
 .../tasks/RedisHash.java}                          |  16 ++-
 .../geode/benchmark/redis/tasks/SetRedisTask.java  |  69 ++++++++++
 .../benchmark/redis/tasks/StartGeodeServer.java    |  52 +++++++
 .../tasks/StartRedisClient.java}                   |  28 +++-
 .../benchmark/redis/tasks/StartRedisServer.java    |  54 ++++++++
 .../tasks/StopRedisClient.java}                    |  20 +--
 .../tasks/StopRedisServer.java}                    |  11 +-
 .../benchmark/redis/tests/RedisBenchmark.java      | 152 +++++++++++++++++++++
 .../tests/RedisGetBenchmark.java}                  |  29 ++--
 .../tests/RedisHgetBenchmark.java}                 |  30 ++--
 .../tests/RedisHsetAndHgetBenchmark.java}          |  36 ++---
 .../tests/RedisHsetBenchmark.java}                 |  30 ++--
 .../tests/RedisSetBenchmark.java}                  |  29 ++--
 .../tests/RedisWeightedHsetAndHgetBenchmark.java}  |  37 +++--
 .../topology/GeodeTopology.java}                   |  30 ++--
 .../redis/topology/ManualRedisTopology.java        |  60 ++++++++
 .../topology/RedisTopology.java}                   |  41 +++---
 .../benchmark/tasks/AbstractPrePopulateRegion.java |  14 +-
 .../geode/benchmark/tasks/CompositeTask.java       |  89 ++++++++++++
 .../geode/benchmark/tasks/CreateIndexOnID.java     |   4 +-
 .../benchmark/tasks/CreatePartitionedRegion.java   |   4 +-
 .../benchmark/tasks/CreateReplicatedRegion.java    |   4 +-
 .../benchmark/tasks/ExecuteFilteredFunction.java   |  24 ++--
 .../geode/benchmark/tasks/ExecuteFunction.java     |  19 ++-
 .../tasks/ExecuteParameterizedFunction.java        |  23 +++-
 .../tasks/{GetTask.java => GetStringTask.java}     |  24 +++-
 .../org/apache/geode/benchmark/tasks/GetTask.java  |  15 +-
 .../org/apache/geode/benchmark/tasks/OQLQuery.java |  42 +++---
 .../geode/benchmark/tasks/PrePopulateRegion.java   |   9 +-
 .../benchmark/tasks/PrePopulateRegionBytes.java    |   9 +-
 .../benchmark/tasks/PrePopulateRegionLong.java     |   9 +-
 ...ateRegion.java => PrePopulateRegionString.java} |  19 ++-
 .../geode/benchmark/tasks/ProcessControl.java      |  43 +++++-
 .../tasks/{GetTask.java => PutStringTask.java}     |  18 ++-
 .../apache/geode/benchmark/tasks/StartLocator.java |   1 +
 .../apache/geode/benchmark/tasks/StartServer.java  |  41 ++++--
 .../apache/geode/benchmark/tasks/StopServer.java   |   4 +-
 .../geode/benchmark/tasks/WeightedTasks.java       | 110 +++++++++++++++
 .../benchmark/tests/AbstractFunctionBenchmark.java |   3 +-
 .../AbstractPerformanceTest.java}                  |  16 ++-
 .../geode/benchmark/tests/GeodeBenchmark.java      |   8 ++
 .../geode/benchmark/tests/NoopBenchmark.java       |   3 +-
 .../tests/P2pPartitionedGetBenchmark.java          |   5 +-
 .../tests/P2pPartitionedPutBenchmark.java          |   3 +-
 .../tests/P2pPartitionedPutBytesBenchmark.java     |   3 +-
 .../tests/P2pPartitionedPutLongBenchmark.java      |   3 +-
 .../PartitionedFunctionExecutionBenchmark.java     |   2 +-
 ...nedFunctionExecutionWithArgumentsBenchmark.java |   3 +-
 ...ionedFunctionExecutionWithFiltersBenchmark.java |   2 +-
 .../benchmark/tests/PartitionedGetBenchmark.java   |   5 +-
 .../tests/PartitionedGetLongBenchmark.java         |   5 +-
 ...ark.java => PartitionedGetStringBenchmark.java} |  14 +-
 .../tests/PartitionedIndexedQueryBenchmark.java    |   5 +-
 .../tests/PartitionedNonIndexedQueryBenchmark.java |   5 +-
 .../tests/PartitionedPutAllBenchmark.java          |   3 +-
 .../tests/PartitionedPutAllLongBenchmark.java      |   3 +-
 .../benchmark/tests/PartitionedPutBenchmark.java   |   3 +-
 .../tests/PartitionedPutBytesBenchmark.java        |   3 +-
 .../tests/PartitionedPutLongBenchmark.java         |   3 +-
 ...ark.java => PartitionedPutStringBenchmark.java} |  13 +-
 .../ReplicatedFunctionExecutionBenchmark.java      |   2 +-
 ...tedFunctionExecutionWithArgumentsBenchmark.java |   3 +-
 ...catedFunctionExecutionWithFiltersBenchmark.java |   2 +-
 .../benchmark/tests/ReplicatedGetBenchmark.java    |   5 +-
 .../tests/ReplicatedGetLongBenchmark.java          |   5 +-
 .../tests/ReplicatedIndexedQueryBenchmark.java     |   5 +-
 .../tests/ReplicatedNonIndexedQueryBenchmark.java  |   5 +-
 .../benchmark/tests/ReplicatedPutAllBenchmark.java |   3 +-
 .../tests/ReplicatedPutAllLongBenchmark.java       |   3 +-
 .../benchmark/tests/ReplicatedPutBenchmark.java    |   3 +-
 .../tests/ReplicatedPutLongBenchmark.java          |   3 +-
 .../benchmark/topology/ClientServerTopology.java   |   6 +-
 .../ClientServerTopologyWithRouterAndSniProxy.java |   6 +-
 .../topology/ClientServerTopologyWithSniProxy.java |  11 +-
 .../geode/benchmark/topology/P2pTopology.java      |   4 +-
 .../org/apache/geode/benchmark/topology/Ports.java |   6 +
 .../apache/geode/benchmark/topology/Topology.java  |  16 ++-
 .../org/apache/geode/benchmark/Constants.java}     |  10 +-
 .../benchmark/junit/CartesianSubclassSource.java   |  54 ++++++++
 .../EnableIfClassExists.java}                      |  34 +++--
 .../junit/EnableIfClassExistsCondition.java        |  58 ++++++++
 .../benchmark/junit/SubclassSourceProvider.java    |  46 +++++++
 .../parameters/GcLoggingParametersTest.java        |  76 +++++------
 .../benchmark/parameters/GcParametersTest.java     | 129 ++++++++---------
 .../benchmark/parameters/HeapParametersTest.java   |  37 ++---
 .../parameters/SafepointLoggingParametersTest.java |  73 ++++++++++
 .../benchmark/redis/tests/RedisBenchmarkTest.java  |  58 ++++++++
 .../redis/topology/GeodeTopologyTest.java          |  75 ++++++++++
 .../redis/topology/ManualRedisTopologyTest.java    |  75 ++++++++++
 .../redis/topology/RedisTopologyTest.java          |  51 +++++++
 .../geode/benchmark/tasks/WeightedTasksTest.java   |  83 +++++++++++
 .../benchmark/tests/ClientServerBenchmarkTest.java |  21 ++-
 .../PartitionedFunctionExecutionBenchmarkTest.java |   3 +
 ...unctionExecutionWithArgumentsBenchmarkTest.java |   1 +
 ...dFunctionExecutionWithFiltersBenchmarkTest.java |   4 +-
 .../tests/PartitionedGetBenchmarkTest.java         |   4 +-
 .../tests/PartitionedGetLongBenchmarkTest.java     |   4 +-
 .../PartitionedIndexedQueryBenchmarkTest.java      |   1 +
 .../PartitionedNonIndexedQueryBenchmarkTest.java   |   1 +
 .../tests/PartitionedPutAllBenchmarkTest.java      |   4 +-
 .../tests/PartitionedPutAllLongBenchmarkTest.java  |   4 +-
 .../tests/PartitionedPutBenchmarkTest.java         |   4 +-
 .../tests/PartitionedPutLongBenchmarkTest.java     |   4 +-
 .../ReplicatedFunctionExecutionBenchmarkTest.java  |   1 +
 ...unctionExecutionWithArgumentsBenchmarkTest.java |   1 +
 ...dFunctionExecutionWithFiltersBenchmarkTest.java |   1 +
 .../tests/ReplicatedGetBenchmarkTest.java          |   4 +-
 .../tests/ReplicatedGetLongBenchmarkTest.java      |   4 +-
 .../tests/ReplicatedIndexedQueryBenchmarkTest.java |   1 +
 .../ReplicatedNonIndexedQueryBenchmarkTest.java    |   1 +
 .../tests/ReplicatedPutBenchmarkTest.java          |   4 +-
 .../tests/ReplicatedPutLongBenchmarkTest.java      |   4 +-
 .../topology/ClientServerTopologyTest.java         |  34 ++---
 .../ClientServerTopologyWithSniProxyTest.java      |  19 +--
 gradle.properties                                  |   2 +-
 gradle/dependency-versions.properties              |  29 ++--
 gradle/wrapper/gradle-wrapper.properties           |   3 +-
 harness/build.gradle                               |   8 +-
 .../org/apache/geode/perftest/PerformanceTest.java |   1 +
 .../java/org/apache/geode/perftest/TestConfig.java |   4 +
 .../org/apache/geode/perftest/TestRunners.java     |  24 ----
 .../perftest/analysis/BenchmarkRunAnalyzer.java    |   2 +-
 .../apache/geode/perftest/jvms/JVMLauncher.java    |  29 +++-
 .../geode/perftest/jvms/RemoteJVMFactory.java      |   2 +-
 .../geode/perftest/runner/DefaultTestRunner.java   |   1 +
 .../geode/perftest/BenchmarkPropertiesTest.java    |   6 +-
 .../geode/perftest/TestRunnerIntegrationTest.java  |   5 +-
 .../infrastructure/ssh/SshInfrastructureTest.java  |   6 +-
 .../geode/perftest/jvms/RemoteJVMFactoryTest.java  |   5 +-
 infrastructure/build.gradle                        |   3 +
 infrastructure/scripts/aws/README.md               |  13 +-
 infrastructure/scripts/aws/copy_to_cluster.sh      |   6 +-
 infrastructure/scripts/aws/launch_cluster.sh       |  41 +++++-
 infrastructure/scripts/aws/run_on_cluster.sh       |   6 +-
 infrastructure/scripts/aws/run_tests.sh            |  14 +-
 .../infrastructure/aws/AwsBenchmarkMetadata.java   |   6 +-
 .../geode/infrastructure/aws/LaunchCluster.java    |  66 +++++++--
 164 files changed, 3103 insertions(+), 862 deletions(-)

diff --git a/README.md b/README.md
index b49a9a9..9b91583 100644
--- a/README.md
+++ b/README.md
@@ -46,28 +46,74 @@ For example:
 ./gradlew benchmark -Phosts=localhost,localhost,localhost,localhost
 ```
 
-Options:
-```
-    -Phosts               : Hosts used by benchmarks on the order of client,locator,server,server (-Phosts=localhost,localhost,localhost,localhost)
-    -PoutputDir           : Results output directory (-PoutputDir=/tmp/results)
-    -PtestJVM             : Path to an alternative JVM for running the client, locator, and servers. If not specified JAVA_HOME will be used. Note all compilation tasks will still use JAVA_HOME.
-    -PwithSsl             : Flag to run geode with SSL. A self-signed certificate will be generated at runtime.
-    -PwithSslProtocols    : Specifies enabled SSL protocols. See Geode property `ssl-protocols`
-    -PwithSslCiphers      : Specifies enabled SSL chipher suites. See Geode property `ssl-ciphers`
-    -PwithSecurityManager : Flag to start Geode with the example implementation of SecurityManager
-    -PwithSniProxy        : Use SNI proxy topology.
-    -PwithSniProxyImage   : Provide an alternative Docker image coordinate for SNI proxy.
-    -PwithRouter          : Use router with SNI proxy topology.
-    -PwithRouterImage     : Provide an alternative Docker image coordinate for router.
-    -PwithGc              : Select which GC to use. Valid values CMS (default), G1, Z.
-    -PwithHeap            : Specify how large a heap the benchmark VMs should use, default "8g". Accepts any `-Xmx` value, like "32g".
-    -PwithThreads         : Specify how many threads to use when executing the benchmark. Default varies by benchmark.
-    -PwithWarmup          : Specify how long to warm up the benchmark in seconds. Default is 60 seconds.
-    -PwithDuration        : Specify how long to measure the benchmark in seconds. Default is 300 seconds.
-    --tests               : Specific benchmarks to run (--tests=PartitionedPutBenchmark)
-    -d                    : Debug
-    -i                    : Info
-```    
+### Options
+The benchmarks can take configuration options. Some using Gradle's `-P` flag and other, which adjust
+benchmark behavior, via Java system properties using `-D`.
+
+| Option                | Description |
+| --------------------- | ----------- |
+| `-Phosts`               | Hosts used by benchmarks on the order of client,locator,server,server (-Phosts=localhost,localhost,localhost,localhost) |
+| `-PoutputDir`           | Results output directory (-PoutputDir=/tmp/results) |
+| `-PtestJVM`             | Path to an alternative JVM for running the client, locator, and servers. If not specified JAVA_HOME will be used. Note all compilation tasks will still use JAVA_HOME. |
+| `-Pbenchmark.X`         | Where X is a benchmark configuration, defined below. |
+| `--tests`               | Specific benchmarks to run (--tests=PartitionedPutBenchmark) |
+| `-d`                    | Debug |
+| `-i`                    | Info |
+
+#### Benchmark Configuration
+##### Common
+These options may apply to all benchmarks.
+
+| Option                | Description |
+| --------------------- | ----------- |
+| withGc                | Select which GC to use. Valid values CMS (default), G1, Z, Shenandoah, Epsilon. |
+| withHeap              | Specify how large a heap the benchmark VMs should use, default "8g". Accepts any `-Xmx` value, like "32g". |
+| withThreads           | Specify how many threads to use when executing the benchmark. Default varies by benchmark. |
+| withWarmup            | Specify how long to warm up the benchmark in seconds. Default is 60 seconds. |
+| withDuration          | Specify how long to run the benchmark in seconds. Default is 300 seconds. |
+| withMinKey            | The minimum key value in the key range. Default is 0. |
+| withMaxKey            | The maximum key value in the key range. Default varies by benchmark. |
+| withLocatorCount      | Number of locators a topology should use. Typically defaults to 1. |
+| withServerCount       | Number of servers a topology should use. Typically defaults to 2. |
+| withClientCount       | Number of clients a topology should use. Typically defaults to 1. |
+| withReplicas          | Number of region replicas. |
+| withAsyncReplication  | Enable asynch region replication. |
+| withNettyThreads      | Number of threads Netty IO Services should have. |
+
+##### Geode Benchmarks
+These options only apply to Geode benchmarks.
+
+| Option                | Description |
+| --------------------- | ----------- |
+| withSsl               | Flag to run geode with SSL. A self-signed certificate will be generated at runtime. |
+| withSslProtocols      | Specifies enabled SSL protocols. See Geode property `ssl-protocols` |
+| withSslCiphers        | Specifies enabled SSL chipher suites. See Geode property `ssl-ciphers` |
+| withSecurityManager   | Flag to start Geode with the example implementation of SecurityManager |
+| withSniProxy          | Use SNI proxy topology. |
+| withSniProxyImage     | Provide an alternative Docker image coordinate for SNI proxy. |
+| withRouter            | Use router with SNI proxy topology. |
+| withRouterImage       | Provide an alternative Docker image coordinate for router. |
+
+##### Redis Benchmarks
+These options only apply to Redis benchmarks.
+
+| Option                | Description |
+| --------------------- | ----------- |
+| withRedisClient       | Redis client to use. May be 'jedis' (default) or 'lettuce'. |
+| withRedisCluster      | Redis cluster implementation. May be 'geode' (default), 'redis', 'manual'. |
+| withRedisServers      | A semicolon delimited list of Redis host:port pairs for manual cluster mode. |
+
+##### Debugging
+These options should not be used when measuring benchmarks.
+
+| Option                | Description |
+| --------------------- | ----------- |
+| withValidation        | Enable validation of operations. Default disabled.|
+| withGcLogging         | Enable GC logging. Default disabled.|
+| withSafepointLogging  | Enable Safepoint logging. Default disabled.|
+| withStrace            | Launch remote JVM via strace for tracing system calls. Default disabled.|
+
+
 ### Scripts for running in aws and analyzing results
 
 This project includes some scripts to automate running benchmarks in AWS and analyzing the results produced (as well as the results produced from running locally). See the 
@@ -94,7 +140,7 @@ reported by the yardstick framework.
 * Benchmark configuration class, which defines the topology of the test and
 * the initialization tasks and workload tasks for the test.
 */
-public class PartitionedPutBenchmark implements PerformanceTest {
+public class PartitionedPutBenchmark extends AbstractPerformanceTest {
 
   @Test
   public void run() throws Exception {
@@ -168,16 +214,16 @@ supported proxy implementations. The value should be set to a valid Docker image
  
 To run a test, e.g. `PartitionedGetBenchmark`, with default SNI Proxy:
 ```console
-./run_tests.sh -t anytagname -- -PwithSniProxy --tests=PartitionedGetBenchmark
+./run_tests.sh -t anytagname -- -Pbenchmark.withSniProxy --tests=PartitionedGetBenchmark
 ```
 
-Since SNI is a feature of TLS, running with the SNI topology incurs TLS overheads with implied `-PwithSsl`.
+Since SNI is a feature of TLS, running with the SNI topology incurs TLS overheads with implied `-Pbenchmark.withSsl`.
 
 ### Router
 An alternative topology uses a router sitting in front of the SNI proxy to simulate off network access
-to the cluster, enabled with `-PwithRouter`.
+to the cluster, enabled with `-Pbenchmark.withRouter`.
 
-Enabling the router implies `-PwithSniProxy`.
+Enabling the router implies `-Pbenchmark.withSniProxy`.
 
 The `withRouter` property accepts:
  * `HAProxy` for HAProxy based router (default).
@@ -185,5 +231,44 @@ The `withRouter` property accepts:
 
 Example:
 ```console
-./run_tests.sh -t anytagname -- -PwithRouter --tests=PartitionedGetBenchmark
+./run_tests.sh -t anytagname -- -Pbenchmark.withRouter --tests=PartitionedGetBenchmark
 ```
+
+## Redis Benchmarking
+
+You can run benchmarks utilizing the Redis protocol with various clients and backends. All Redis
+benchmarks take the pattern `Redis*Benchmark`. They expect 3 shards with 1 replica per shard, which
+when combined with 1 Geode locator and the benchmarking client needs a total of 8 hosts 
+(`./launch_cluster ... -c 8`).
+
+The `withRedisClient` property accepts:
+* `Jedis` for using the [Jedis](https://github.com/redis/jedis) library (default).
+* `Lettuce` for using the [Lettuce](https://lettuce.io) library.
+
+The `withRedisCluster` property accepts:
+* `Geode` for using the [Geode](https://geode.apache.org) server backend (default). Builds a Geode
+  cluster utilizing 7 hosts, 1 locator and 6 servers.
+* `Redis` for using the [Redis](https://redis.io) server backend. Builds a Redis cluster utilizing
+  6 hosts and the [Bitnami Redis image](https://hub.docker.com/r/bitnami/redis/).
+* `Manual` for using a manually configured Redis server backend, like [Elasticache](https://aws.amazon.com/elasticache/).
+  Use `withRedisServers` to specify the address(es) to the Redis server endpoints.
+  
+Examples:
+
+* Runs the `RedisGetBenchmark` against a Geode cluster using the Jedis client.
+    ```console
+    ./run_tests.sh -t anytagname -- --tests=RedisGetBenchmark
+    ```
+* Runs the `RedisGetBenchmark` against a Geode cluster using the Lettuce client.
+    ```console
+    ./run_tests.sh -t anytagname -- -Pbenchmark.withRedisClient=lettuce --tests=RedisGetBenchmark
+    ```
+* Runs the `RedisGetBenchmark` against a Redis cluster using the Jedis client.
+    ```console
+    ./run_tests.sh -t anytagname -- -Pbenchmark.withRedisCluster=redis --tests=RedisGetBenchmark
+    ```
+* Runs the `RedisGetBenchmark` against an Elasticache cluster using the Jedis client.
+    ```console
+    ./run_tests.sh -t anytagname -- -Pbenchmark.withRedisCluster=manual -Pbenchmark.withRedisServers=my-cluster...clustercfg.usw2.cache.amazonaws.com:6379 --tests=RedisGetBenchmark
+    ```
+  
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index f17d9e9..74d349a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,10 +16,11 @@
  */
 
 plugins {
-  id "io.spring.dependency-management" version "1.0.10.RELEASE"
-  id "com.bmuschko.docker-remote-api" version "6.6.1"
-  id "com.diffplug.spotless" version "5.6.1"
+  id "io.spring.dependency-management" version "1.0.11.RELEASE"
+  id "com.bmuschko.docker-remote-api" version "6.7.0"
+  id "com.diffplug.spotless" version "5.11.1"
   id "org.nosphere.apache.rat" version "0.7.0"
+  id "com.github.ben-manes.versions" version "0.38.0"
 }
 
 apply plugin: 'com.bmuschko.docker-remote-api'
diff --git a/geode-benchmarks/build.gradle b/geode-benchmarks/build.gradle
index e431946..4234c75 100644
--- a/geode-benchmarks/build.gradle
+++ b/geode-benchmarks/build.gradle
@@ -17,13 +17,11 @@
 
 import org.gradle.util.VersionNumber
 
-plugins { id 'java' }
+plugins { id 'java-library' }
 
 group 'org.apache.geode-benchmark'
 version '1.0-SNAPSHOT'
 
-sourceCompatibility = 1.8
-
 def outputDir = project.hasProperty('outputDir') ? project.findProperty('outputDir') : new File(project.buildDir, "benchmarks_" + getDate()).getAbsolutePath()
 def geodeVersion = project.hasProperty('geodeVersion') ? project.findProperty('geodeVersion') : '1.+'
 
@@ -37,30 +35,62 @@ repositories {
    This is used in CI to benchmark various new/old versions of Geode.
    Also useful in dev where you can clone geode and publishToMavenLocal
    */
-  mavenLocal()
+  mavenLocal() {
+    metadataSources {
+      mavenPom()
+      ignoreGradleMetadataRedirection()
+    }
+  }
   // fall back to mavenCentral, which has lots of released versions of Geode
-  mavenCentral()
+  mavenCentral() {
+    metadataSources {
+      mavenPom()
+      ignoreGradleMetadataRedirection()
+    }
+  }
+}
+
+configurations {
+  geodeVersionResolver
 }
 
 dependencies {
-  implementation(group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: project.'junit-jupiter-engine.version')
+  geodeVersionResolver(group: 'org.apache.geode', name: 'geode-core', version: geodeVersion)
+  geodeVersion = configurations.geodeVersionResolver.resolvedConfiguration.resolvedArtifacts.find {it.name == 'geode-core'}.moduleVersion.id.version
+  println "Building with Geode ${geodeVersion}."
+
+  implementation platform("org.apache.geode:geode-all-bom:${geodeVersion}")
+
+  implementation(project(':harness'))
+
+  implementation(group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: project.'junit-jupiter.version')
   implementation(group: 'org.junit-pioneer', name: 'junit-pioneer', version: project.'junit-pioneer.version')
+  implementation(group: 'org.assertj', name: 'assertj-core', version: project.'assertj-core.version')
+
   implementation(group: 'org.slf4j', name: 'slf4j-simple', version: project.'slf4j-simple.version')
-  implementation(project(':harness'))
 
-  implementation(group: 'org.apache.geode', name: 'geode-core', version: geodeVersion)
-  if (VersionNumber.parse(geodeVersion) >= VersionNumber.parse("1.11.0")) {
-    runtime(group: 'org.apache.geode', name: 'geode-log4j', version: geodeVersion)
+  implementation(group: 'org.apache.geode', name: 'geode-core')
+  if (VersionNumber.parse(geodeVersion) >= VersionNumber.parse("1.11.0.+")) {
+    runtimeOnly(group: 'org.apache.geode', name: 'geode-log4j')
   } else {
-    runtime(group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.13.3')
+    runtimeOnly(group: 'org.apache.logging.log4j', name: 'log4j-core')
+  }
+
+  implementation(group: 'redis.clients', name: 'jedis', version: project.'jedis.version')
+  implementation(group: 'io.lettuce', name: 'lettuce-core', version: project.'lettuce.version') {
+    exclude group: 'io.netty'
+  }
+
+  if (VersionNumber.parse(geodeVersion) >= VersionNumber.parse("1.15.0.+")) {
+    runtimeOnly(group: 'org.apache.geode', name: 'geode-apis-compatible-with-redis')
   }
 
   // Required for missing dependency on geode-core.
-  runtime(group: 'org.eclipse.jetty', name: 'jetty-webapp', version: '9.4.12.v20180830')
+  runtimeOnly(group: 'org.eclipse.jetty', name: 'jetty-webapp')
 
-  testImplementation(group: 'org.junit.jupiter', name: 'junit-jupiter-params', version:project.'junit-jupiter-engine.version')
-  testImplementation(group: 'org.mockito', name: 'mockito-all', version: project.'mockito-all.version')
-  testImplementation(group: 'org.assertj', name: 'assertj-core', version: project.'assertj-core.version')
+  testImplementation(group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: project.'junit-jupiter.version')
+  testImplementation(group: 'org.mockito', name: 'mockito-core', version: project.'mockito.version')
+  testImplementation(group: 'io.github.classgraph', name: 'classgraph', version: project.'classgraph.version')
 }
 
 compileJava {
@@ -152,7 +182,6 @@ task benchmark(type: Test) {
 
   systemProperty 'benchmark.withSecurityManager', project.hasProperty('withSecurityManager')
 
-
   doFirst {
     if(!project.hasProperty('hosts')) {
       throw new GradleException("You must set the hosts property to a comma separated list of hosts. Eg ./gradlew benchmark -Phosts=localhost,localhost,localhost")
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java
index 922ff51..10a8630 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java
@@ -16,5 +16,5 @@
 package org.apache.geode.benchmark.parameters;
 
 public enum GcImplementation {
-  CMS, G1, Z, Shenandoah;
+  CMS, G1, Z, Shenandoah, Epsilon;
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcLoggingParameters.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcLoggingParameters.java
index e19482e..9092e8f 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcLoggingParameters.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcLoggingParameters.java
@@ -15,6 +15,7 @@
 
 package org.apache.geode.benchmark.parameters;
 
+import static java.lang.Boolean.getBoolean;
 import static org.apache.geode.benchmark.parameters.JavaVersion.v11;
 import static org.apache.geode.benchmark.parameters.Utils.configureGeodeProductJvms;
 
@@ -26,23 +27,26 @@ import org.apache.geode.perftest.TestConfig;
 public class GcLoggingParameters {
   private static final Logger logger = LoggerFactory.getLogger(GcLoggingParameters.class);
 
+  public static final String WITH_GC_LOGGING = "benchmark.withGcLogging";
+
   public static void configure(final TestConfig testConfig) {
-    final JavaVersion javaVersion = JavaVersion.current();
-    logger.info("Configuring GC logging parameters for Java {}.", javaVersion);
-    if (javaVersion.atLeast(v11)) {
-      configureGeodeProductJvms(testConfig, "-Xlog:gc*:OUTPUT_DIR/gc.log");
-    } else {
-      configureGeodeProductJvms(testConfig,
-          "-XX:+PrintGCDetails",
-          "-XX:+PrintGCTimeStamps",
-          "-XX:+PrintGCDateStamps",
-          "-XX:+PrintGCApplicationStoppedTime",
-          "-XX:+PrintGCApplicationConcurrentTime",
-          "-XX:+UseGCLogFileRotation",
-          "-XX:NumberOfGCLogFiles=20",
-          "-XX:GCLogFileSize=1M",
-          "-Xloggc:OUTPUT_DIR/gc.log");
+    if (getBoolean(WITH_GC_LOGGING)) {
+      final JavaVersion javaVersion = JavaVersion.current();
+      logger.info("Configuring GC logging parameters for Java {}.", javaVersion);
+      if (javaVersion.atLeast(v11)) {
+        configureGeodeProductJvms(testConfig, "-Xlog:gc*:OUTPUT_DIR/gc.log");
+      } else {
+        configureGeodeProductJvms(testConfig,
+            "-XX:+PrintGCDetails",
+            "-XX:+PrintGCTimeStamps",
+            "-XX:+PrintGCDateStamps",
+            "-XX:+PrintGCApplicationStoppedTime",
+            "-XX:+PrintGCApplicationConcurrentTime",
+            "-XX:+UseGCLogFileRotation",
+            "-XX:NumberOfGCLogFiles=20",
+            "-XX:GCLogFileSize=1M",
+            "-Xloggc:OUTPUT_DIR/gc.log");
+      }
     }
   }
-
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcParameters.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcParameters.java
index 9398e5c..90090c7 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcParameters.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcParameters.java
@@ -43,9 +43,19 @@ public class GcParameters {
       case Shenandoah:
         configureShenandoah(testConfig);
         break;
+      case Epsilon:
+        configureEpsilon(testConfig);
+        break;
     }
   }
 
+  private static void configureEpsilon(final TestConfig testConfig) {
+    configureGeodeProductJvms(testConfig,
+        "-XX:+UnlockExperimentalVMOptions",
+        "-XX:+UseEpsilonGC",
+        "-XX:+UseNUMA");
+  }
+
   private static void configureShenandoah(final TestConfig testConfig) {
     configureGeodeProductJvms(testConfig,
         "-XX:+UnlockExperimentalVMOptions",
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GeodeProperties.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GeodeProperties.java
index deb575d..2ee354a 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GeodeProperties.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GeodeProperties.java
@@ -14,6 +14,7 @@
  */
 package org.apache.geode.benchmark.parameters;
 
+import static java.lang.String.valueOf;
 import static org.apache.geode.benchmark.topology.Topology.WITH_SECURITY_MANAGER_PROPERTY;
 import static org.apache.geode.benchmark.topology.Topology.WITH_SSL_CIPHERS_PROPERTY;
 import static org.apache.geode.benchmark.topology.Topology.WITH_SSL_PROPERTY;
@@ -24,7 +25,6 @@ import static org.apache.geode.distributed.ConfigurationProperties.CONSERVE_SOCK
 import static org.apache.geode.distributed.ConfigurationProperties.DISTRIBUTED_SYSTEM_ID;
 import static org.apache.geode.distributed.ConfigurationProperties.ENABLE_CLUSTER_CONFIGURATION;
 import static org.apache.geode.distributed.ConfigurationProperties.ENABLE_TIME_STATISTICS;
-import static org.apache.geode.distributed.ConfigurationProperties.LOCATOR_WAIT_TIME;
 import static org.apache.geode.distributed.ConfigurationProperties.LOG_DISK_SPACE_LIMIT;
 import static org.apache.geode.distributed.ConfigurationProperties.LOG_FILE_SIZE_LIMIT;
 import static org.apache.geode.distributed.ConfigurationProperties.LOG_LEVEL;
@@ -42,42 +42,45 @@ import static org.apache.geode.security.SecurableCommunicationChannels.ALL;
 import java.util.Properties;
 
 import org.apache.geode.benchmark.security.ExampleAuthInit;
+import org.apache.geode.distributed.ConfigurationProperties;
 
 public class GeodeProperties {
 
   public static Properties serverProperties() {
-    Properties properties = new Properties();
+    final Properties properties = new Properties();
 
-    properties.setProperty(CONSERVE_SOCKETS, "false");
-    properties.setProperty(ENABLE_TIME_STATISTICS, "true");
-    properties.setProperty(LOCATOR_WAIT_TIME, "120");
-    properties.setProperty(LOG_DISK_SPACE_LIMIT, "100");
-    properties.setProperty(LOG_FILE_SIZE_LIMIT, "10");
+    properties.setProperty(CONSERVE_SOCKETS, valueOf(false));
+    properties.setProperty(ENABLE_TIME_STATISTICS, valueOf(true));
+    properties.setProperty(LOG_DISK_SPACE_LIMIT, valueOf(100));
+    properties.setProperty(LOG_FILE_SIZE_LIMIT, valueOf(10));
     properties.setProperty(LOG_LEVEL, "config");
-    properties.setProperty(REMOVE_UNRESPONSIVE_CLIENT, "true");
-    properties.setProperty(STATISTIC_SAMPLING_ENABLED, "true");
-    properties.setProperty(ARCHIVE_DISK_SPACE_LIMIT, "150");
-    properties.setProperty(ARCHIVE_FILE_SIZE_LIMIT, "10");
-    properties.setProperty(DISTRIBUTED_SYSTEM_ID, "0");
-    properties.setProperty(ENABLE_CLUSTER_CONFIGURATION, "false");
-    properties.setProperty(USE_CLUSTER_CONFIGURATION, "false");
+    properties.setProperty(REMOVE_UNRESPONSIVE_CLIENT, valueOf(true));
+    properties.setProperty(STATISTIC_SAMPLING_ENABLED, valueOf(true));
+    properties.setProperty(ARCHIVE_DISK_SPACE_LIMIT, valueOf(150));
+    properties.setProperty(ARCHIVE_FILE_SIZE_LIMIT, valueOf(10));
+    properties.setProperty(DISTRIBUTED_SYSTEM_ID, valueOf(0));
+    properties.setProperty(ENABLE_CLUSTER_CONFIGURATION, valueOf(false));
+    properties.setProperty(USE_CLUSTER_CONFIGURATION, valueOf(false));
     properties.setProperty(SERIALIZABLE_OBJECT_FILTER, "benchmark.geode.data.**");
+    properties.setProperty(MEMBER_TIMEOUT, valueOf(600000));
 
     return withOptions(properties);
   }
 
   public static Properties locatorProperties() {
-    // Locator properties are the same as the server properties right now
+    final Properties properties = serverProperties();
+    properties.setProperty(ConfigurationProperties.LOCATOR_WAIT_TIME, valueOf(0));
+
     return withOptions(serverProperties());
   }
 
   public static Properties clientProperties() {
-    Properties properties = new Properties();
+    final Properties properties = new Properties();
 
-    properties.setProperty(ENABLE_TIME_STATISTICS, "true");
+    properties.setProperty(ENABLE_TIME_STATISTICS, valueOf(true));
     properties.setProperty(LOG_LEVEL, "config");
-    properties.setProperty(STATISTIC_SAMPLING_ENABLED, "true");
-    properties.setProperty(MEMBER_TIMEOUT, "8000");
+    properties.setProperty(STATISTIC_SAMPLING_ENABLED, valueOf(true));
+    properties.setProperty(MEMBER_TIMEOUT, valueOf(8000));
 
     properties.setProperty("security-username", "superUser");
     properties.setProperty("security-password", "123");
@@ -86,7 +89,7 @@ public class GeodeProperties {
     return withOptions(properties);
   }
 
-  public static Properties withSecurityManager(Properties properties) {
+  public static Properties withSecurityManager(final Properties properties) {
     properties.setProperty(SECURITY_MANAGER,
         "org.apache.geode.examples.security.ExampleSecurityManager");
     properties.setProperty("security-username", "superUser");
@@ -94,7 +97,7 @@ public class GeodeProperties {
     return properties;
   }
 
-  public static Properties withSsl(Properties properties) {
+  public static Properties withSsl(final Properties properties) {
     properties.setProperty(SSL_ENABLED_COMPONENTS, ALL);
     final String withSslProtocols = System.getProperty(WITH_SSL_PROTOCOLS_PROPERTY);
     if (!isBlank(withSslProtocols)) {
@@ -121,7 +124,7 @@ public class GeodeProperties {
 
   private static boolean isPropertySet(final String propertyName) {
     final String propertyValue = System.getProperty(propertyName);
-    return propertyValue != null && propertyValue.equals("true");
+    return propertyValue != null && propertyValue.equals(valueOf(true));
   }
 
   private static Properties withOptions(Properties properties) {
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/JavaVersion.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/JavaVersion.java
index ffa4175..f7e182b 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/JavaVersion.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/JavaVersion.java
@@ -16,7 +16,7 @@
 package org.apache.geode.benchmark.parameters;
 
 public enum JavaVersion {
-  v8, v11, v12, v13;
+  v8, v11, v12, v13, v16;
 
   public static JavaVersion current() {
     return valueOfVersion(System.getProperty("java.runtime.version"));
@@ -31,6 +31,8 @@ public enum JavaVersion {
       return v12;
     } else if (javaVersion.matches("^13\\b.*")) {
       return v13;
+    } else if (javaVersion.matches("^16\\b.*")) {
+      return v16;
     }
     throw new IllegalStateException("Unknown version " + javaVersion);
   }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/JvmParameters.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/JvmParameters.java
index d26a11a..ec70389 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/JvmParameters.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/JvmParameters.java
@@ -35,6 +35,14 @@ public class JvmParameters {
         "-Dgemfire.OSProcess.ENABLE_OUTPUT_REDIRECTION=true",
         "-Dgemfire.launcher.registerSignalHandlers=true",
         "-XX:+DisableExplicitGC");
+
+    final JavaVersion javaVersion = JavaVersion.current();
+    if (javaVersion.atLeast(JavaVersion.v11)) {
+      configureGeodeProductJvms(testConfig,
+          "-XX:-ThreadLocalHandshakes",
+          "-XX:+UnlockExperimentalVMOptions",
+          "-XX:MonitorUsedDeflationThreshold=0");
+    }
   }
 
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcLoggingParameters.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/SafepointLoggingParameters.java
similarity index 59%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcLoggingParameters.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/SafepointLoggingParameters.java
index e19482e..77cf088 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcLoggingParameters.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/SafepointLoggingParameters.java
@@ -15,6 +15,7 @@
 
 package org.apache.geode.benchmark.parameters;
 
+import static java.lang.Boolean.getBoolean;
 import static org.apache.geode.benchmark.parameters.JavaVersion.v11;
 import static org.apache.geode.benchmark.parameters.Utils.configureGeodeProductJvms;
 
@@ -23,25 +24,19 @@ import org.slf4j.LoggerFactory;
 
 import org.apache.geode.perftest.TestConfig;
 
-public class GcLoggingParameters {
-  private static final Logger logger = LoggerFactory.getLogger(GcLoggingParameters.class);
+public class SafepointLoggingParameters {
+  private static final Logger logger = LoggerFactory.getLogger(SafepointLoggingParameters.class);
+
+  public static final String WITH_SAFEPOINT_LOGGING = "benchmark.withSafepointLogging";
+  public static final String XLOG_SAFEPOINT = "-Xlog:safepoint*:OUTPUT_DIR/safepoint.log";
 
   public static void configure(final TestConfig testConfig) {
-    final JavaVersion javaVersion = JavaVersion.current();
-    logger.info("Configuring GC logging parameters for Java {}.", javaVersion);
-    if (javaVersion.atLeast(v11)) {
-      configureGeodeProductJvms(testConfig, "-Xlog:gc*:OUTPUT_DIR/gc.log");
-    } else {
-      configureGeodeProductJvms(testConfig,
-          "-XX:+PrintGCDetails",
-          "-XX:+PrintGCTimeStamps",
-          "-XX:+PrintGCDateStamps",
-          "-XX:+PrintGCApplicationStoppedTime",
-          "-XX:+PrintGCApplicationConcurrentTime",
-          "-XX:+UseGCLogFileRotation",
-          "-XX:NumberOfGCLogFiles=20",
-          "-XX:GCLogFileSize=1M",
-          "-Xloggc:OUTPUT_DIR/gc.log");
+    if (getBoolean(WITH_SAFEPOINT_LOGGING)) {
+      final JavaVersion javaVersion = JavaVersion.current();
+      if (javaVersion.atLeast(v11)) {
+        logger.info("Configuring safepoint logging parameters for Java {}.", javaVersion);
+        configureGeodeProductJvms(testConfig, XLOG_SAFEPOINT);
+      }
     }
   }
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/JvmParameters.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/parameters/GeodeParameters.java
similarity index 52%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/JvmParameters.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/parameters/GeodeParameters.java
index d26a11a..74d152f 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/JvmParameters.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/parameters/GeodeParameters.java
@@ -13,28 +13,29 @@
  * the License.
  */
 
-package org.apache.geode.benchmark.parameters;
+package org.apache.geode.benchmark.redis.parameters;
 
-import static org.apache.geode.benchmark.parameters.Utils.configureGeodeProductJvms;
+import static java.lang.Integer.getInteger;
+import static java.lang.String.format;
+import static org.apache.geode.benchmark.tests.GeodeBenchmark.WITH_BUCKETS;
+import static org.apache.geode.benchmark.tests.GeodeBenchmark.WITH_REPLICAS;
+import static org.apache.geode.benchmark.topology.Roles.SERVER;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.apache.geode.perftest.TestConfig;
 
-public class JvmParameters {
-  private static final Logger logger = LoggerFactory.getLogger(JvmParameters.class);
+public class GeodeParameters {
+  private static final Logger logger = LoggerFactory.getLogger(GeodeParameters.class);
 
   public static void configure(final TestConfig testConfig) {
-    logger.info("Configuring JVM parameters.");
-
-    configureGeodeProductJvms(testConfig,
-        "-server",
-        "-Djava.awt.headless=true",
-        "-Dsun.rmi.dgc.server.gcInterval=9223372036854775806",
-        "-Dgemfire.OSProcess.ENABLE_OUTPUT_REDIRECTION=true",
-        "-Dgemfire.launcher.registerSignalHandlers=true",
-        "-XX:+DisableExplicitGC");
+    logger.info("Configuring Geode APIs compatible with Redis parameters.");
+
+    testConfig.jvmArgs(SERVER.name(), "-Denable-unsupported-commands=true",
+        format("-Dredis.replicas=%d", getInteger(WITH_REPLICAS, 1)),
+        format("-Dredis.region.buckets=%d", getInteger(WITH_BUCKETS, 128)),
+        format("-Djava.lang.Integer.IntegerCache.high=%d", getInteger(WITH_BUCKETS, 128)));
   }
 
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/JvmParameters.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/parameters/NettyParameters.java
similarity index 59%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/JvmParameters.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/parameters/NettyParameters.java
index d26a11a..060d1fe 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/JvmParameters.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/parameters/NettyParameters.java
@@ -13,28 +13,28 @@
  * the License.
  */
 
-package org.apache.geode.benchmark.parameters;
+package org.apache.geode.benchmark.redis.parameters;
 
-import static org.apache.geode.benchmark.parameters.Utils.configureGeodeProductJvms;
+import static java.lang.Integer.getInteger;
+import static java.lang.String.format;
+import static org.apache.geode.benchmark.topology.Roles.SERVER;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.apache.geode.perftest.TestConfig;
 
-public class JvmParameters {
-  private static final Logger logger = LoggerFactory.getLogger(JvmParameters.class);
+public class NettyParameters {
+  private static final Logger logger = LoggerFactory.getLogger(NettyParameters.class);
+  public static final String WITH_NETTY_THREADS = "benchmark.withNettyThreads";
 
   public static void configure(final TestConfig testConfig) {
-    logger.info("Configuring JVM parameters.");
-
-    configureGeodeProductJvms(testConfig,
-        "-server",
-        "-Djava.awt.headless=true",
-        "-Dsun.rmi.dgc.server.gcInterval=9223372036854775806",
-        "-Dgemfire.OSProcess.ENABLE_OUTPUT_REDIRECTION=true",
-        "-Dgemfire.launcher.registerSignalHandlers=true",
-        "-XX:+DisableExplicitGC");
+    logger.info("Configuring Netty parameters.");
+
+    final Integer withNettyThreads = getInteger(WITH_NETTY_THREADS, null);
+    if (null != withNettyThreads) {
+      testConfig.jvmArgs(SERVER.name(), format("-Dio.netty.eventLoopThreads=%d", withNettyThreads));
+    }
   }
 
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/AbstractPrePopulate.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/AbstractPrePopulate.java
new file mode 100644
index 0000000..9b09457
--- /dev/null
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/AbstractPrePopulate.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geode.benchmark.redis.tasks;
+
+import static java.util.stream.Collectors.toList;
+import static org.apache.geode.benchmark.topology.Roles.CLIENT;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.geode.benchmark.LongRange;
+import org.apache.geode.perftest.Task;
+import org.apache.geode.perftest.TestContext;
+
+public abstract class AbstractPrePopulate implements Task {
+  private static final Logger logger = LoggerFactory.getLogger(AbstractPrePopulate.class);
+
+  private final RedisClientManager redisClientManager;
+  private final LongRange keyRangeToPrepopulate;
+
+  public AbstractPrePopulate(
+      final RedisClientManager redisClientManager,
+      final LongRange keyRangeToPrepopulate) {
+    this.redisClientManager = redisClientManager;
+    this.keyRangeToPrepopulate = keyRangeToPrepopulate;
+  }
+
+  @Override
+  public void run(final TestContext context) throws Exception {
+    final List<Integer> hostsIDsForRole =
+        context.getHostsIDsForRole(CLIENT.name()).stream().sorted().collect(toList());
+    final int self = context.getJvmID();
+    final int position = hostsIDsForRole.indexOf(self);
+
+    final LongRange keyRange = keyRangeToPrepopulate.sliceFor(hostsIDsForRole.size(), position);
+
+    final int numThreads = Runtime.getRuntime().availableProcessors();
+    final ExecutorService threadPool = Executors.newFixedThreadPool(numThreads);
+    final List<CompletableFuture<Void>> futures = new ArrayList<>();
+
+    for (final LongRange slice : keyRange.slice(numThreads)) {
+      futures.add(CompletableFuture.runAsync(() -> {
+        logger.info("Prepopulating slice: {} starting...", slice);
+        final RedisClient redisClient = redisClientManager.get();
+        slice.forEach(i -> {
+          prepopulate(redisClient, i);
+        });
+      }, threadPool));
+    }
+
+    futures.forEach(CompletableFuture::join);
+
+    threadPool.shutdownNow();
+    threadPool.awaitTermination(5, TimeUnit.MINUTES);
+  }
+
+  protected abstract void prepopulate(final RedisClient redisClient, final long key);
+}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/CreateRedisCluster.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/CreateRedisCluster.java
new file mode 100644
index 0000000..1181dcd
--- /dev/null
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/CreateRedisCluster.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geode.benchmark.redis.tasks;
+
+import static java.lang.String.valueOf;
+import static java.util.Arrays.asList;
+import static java.util.stream.Collectors.toList;
+import static org.apache.geode.benchmark.tasks.ProcessControl.retryUntilZeroExit;
+import static org.apache.geode.benchmark.topology.Ports.REDIS_PORT;
+import static org.apache.geode.benchmark.topology.Roles.CLIENT;
+import static org.apache.geode.benchmark.topology.Roles.SERVER;
+
+import java.net.InetAddress;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.geode.perftest.Task;
+import org.apache.geode.perftest.TestContext;
+
+public class CreateRedisCluster implements Task {
+
+  private static final Logger logger = LoggerFactory.getLogger(CreateRedisCluster.class);
+
+  final int replicas;
+
+  public CreateRedisCluster(final int replicas) {
+    this.replicas = replicas;
+  }
+
+  public int getReplicas() {
+    return replicas;
+  }
+
+  @Override
+  public void run(final TestContext context) throws Exception {
+    final List<Integer> hostsIDsForRole =
+        context.getHostsIDsForRole(CLIENT.name()).stream().sorted().collect(toList());
+    final int self = context.getJvmID();
+    final int position = hostsIDsForRole.indexOf(self);
+
+    if (0 != position) {
+      return;
+    }
+
+    final Set<InetAddress> servers = context.getHostsForRole(SERVER.name());
+
+    final List<String> redisNodes =
+        servers.stream().map(i -> i.getHostAddress() + ":" + REDIS_PORT)
+            .collect(Collectors.toList());
+
+    final ProcessBuilder processBuilder =
+        new ProcessBuilder().command("docker", "run", "--rm",
+            "--network", "host",
+            "bitnami/redis-cluster:latest",
+            "redis-cli",
+            "--cluster", "create");
+
+    processBuilder.command().addAll(redisNodes);
+
+    processBuilder.command().addAll(asList(
+        "--cluster-replicas", valueOf(replicas),
+        "--cluster-yes"));
+
+    logger.info("Creating redis cluster. {}", processBuilder.command());
+
+    retryUntilZeroExit(processBuilder);
+
+    Thread.sleep(10_000);
+  }
+
+}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/FlushDbTask.java
similarity index 73%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/FlushDbTask.java
index 6ffd348..da357fa 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/FlushDbTask.java
@@ -15,21 +15,25 @@
  * limitations under the License.
  */
 
-package org.apache.geode.benchmark.tasks;
+package org.apache.geode.benchmark.redis.tasks;
 
-import org.apache.geode.cache.Cache;
 import org.apache.geode.perftest.Task;
 import org.apache.geode.perftest.TestContext;
 
 /**
- * Task to stop the server.
+ * Flushes the contents of the Redis database.
  */
-public class StopServer implements Task {
+public class FlushDbTask implements Task {
+
+  private final RedisClientManager redisClientManager;
+
+  public FlushDbTask(final RedisClientManager redisClientManager) {
+    this.redisClientManager = redisClientManager;
+  }
 
   @Override
   public void run(final TestContext context) throws Exception {
-    final Cache cache = (Cache) context.getAttribute("SERVER_CACHE");
-    cache.close();
+    redisClientManager.get().flushdb();
   }
 
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/GetRedisTask.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/GetRedisTask.java
new file mode 100644
index 0000000..d0832dd
--- /dev/null
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/GetRedisTask.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geode.benchmark.redis.tasks;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.yardstickframework.BenchmarkConfiguration;
+import org.yardstickframework.BenchmarkDriverAdapter;
+
+import org.apache.geode.benchmark.LongRange;
+
+
+public class GetRedisTask extends BenchmarkDriverAdapter implements Serializable {
+  private static final Logger logger = LoggerFactory.getLogger(GetRedisTask.class);
+
+  private final RedisClientManager redisClientManager;
+  private final LongRange keyRange;
+  private final boolean validate;
+
+  private LongStringCache keyCache;
+  private transient RedisClient redisClient;
+
+  public GetRedisTask(final RedisClientManager redisClientManager, final LongRange keyRange,
+      final boolean validate) {
+    logger.info("Initialized: keyRange={}, validate={}", keyRange, validate);
+    this.redisClientManager = redisClientManager;
+    this.keyRange = keyRange;
+    this.validate = validate;
+  }
+
+  @Override
+  public void setUp(final BenchmarkConfiguration cfg) throws Exception {
+    super.setUp(cfg);
+
+    keyCache = new LongStringCache(keyRange);
+    redisClient = redisClientManager.get();
+  }
+
+  @Override
+  public boolean test(final Map<Object, Object> ctx) throws Exception {
+    final String key = keyCache.valueOf(keyRange.random());
+    final String value = redisClient.get(key);
+    if (validate) {
+      assertThat(value).isEqualTo(key);
+    }
+    return true;
+  }
+
+}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/HgetRedisTask.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/HgetRedisTask.java
new file mode 100644
index 0000000..6b1a3a5
--- /dev/null
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/HgetRedisTask.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geode.benchmark.redis.tasks;
+
+import static org.apache.geode.benchmark.redis.tasks.RedisHash.toField;
+import static org.apache.geode.benchmark.redis.tasks.RedisHash.toKey;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.yardstickframework.BenchmarkConfiguration;
+import org.yardstickframework.BenchmarkDriverAdapter;
+
+import org.apache.geode.benchmark.LongRange;
+
+public class HgetRedisTask extends BenchmarkDriverAdapter implements Serializable {
+  private static final Logger logger = LoggerFactory.getLogger(HgetRedisTask.class);
+
+  private final RedisClientManager redisClientManager;
+  private final LongRange keyRange;
+  private final boolean validate;
+
+  private transient LongStringCache keyCache;
+  private transient RedisClient redisClient;
+
+  public HgetRedisTask(final RedisClientManager redisClientManager, final LongRange keyRange,
+      final boolean validate) {
+    logger.info("Initialized: keyRange={}, validate={}", keyRange, validate);
+    this.redisClientManager = redisClientManager;
+    this.keyRange = keyRange;
+    this.validate = validate;
+  }
+
+  @Override
+  public void setUp(final BenchmarkConfiguration cfg) throws Exception {
+    super.setUp(cfg);
+
+    keyCache = new LongStringCache(keyRange);
+    redisClient = redisClientManager.get();
+  }
+
+  @Override
+  public boolean test(final Map<Object, Object> ctx) throws Exception {
+    final long k = keyRange.random();
+
+    final String key = keyCache.valueOf(toKey(k));
+    final String field = keyCache.valueOf(toField(k));
+    final String value = redisClient.hget(key, field);
+    if (validate) {
+      assertThat(value).isEqualTo(field);
+    }
+    return true;
+  }
+
+}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/HsetRedisTask.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/HsetRedisTask.java
new file mode 100644
index 0000000..6c902e8
--- /dev/null
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/HsetRedisTask.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geode.benchmark.redis.tasks;
+
+
+import static org.apache.geode.benchmark.redis.tasks.RedisHash.toField;
+import static org.apache.geode.benchmark.redis.tasks.RedisHash.toKey;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.yardstickframework.BenchmarkConfiguration;
+import org.yardstickframework.BenchmarkDriverAdapter;
+
+import org.apache.geode.benchmark.LongRange;
+
+public class HsetRedisTask extends BenchmarkDriverAdapter implements Serializable {
+  private static final Logger logger = LoggerFactory.getLogger(HsetRedisTask.class);
+
+  private final RedisClientManager redisClientManager;
+  private final LongRange keyRange;
+
+  private transient LongStringCache keyCache;
+  private transient RedisClient redisClient;
+
+  public HsetRedisTask(final RedisClientManager redisClientManager, final LongRange keyRange) {
+    logger.info("Initialized: keyRange={}", keyRange);
+    this.redisClientManager = redisClientManager;
+    this.keyRange = keyRange;
+  }
+
+  @Override
+  public void setUp(final BenchmarkConfiguration cfg) throws Exception {
+    super.setUp(cfg);
+
+    keyCache = new LongStringCache(keyRange);
+    redisClient = redisClientManager.get();
+  }
+
+  @Override
+  public boolean test(final Map<Object, Object> ctx) throws Exception {
+    final long k = keyRange.random();
+    final String key = keyCache.valueOf(toKey(k));
+    final String field = keyCache.valueOf(toField(k));
+    final String value = keyCache.valueOf(k);
+    redisClient.hset(key, field, value);
+    return true;
+  }
+
+}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/InitRedisServersAttribute.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/InitRedisServersAttribute.java
new file mode 100644
index 0000000..d3c72d2
--- /dev/null
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/InitRedisServersAttribute.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geode.benchmark.redis.tasks;
+
+
+import static org.apache.geode.benchmark.topology.Ports.REDIS_PORT;
+import static org.apache.geode.benchmark.topology.Roles.SERVER;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.geode.benchmark.redis.tests.RedisBenchmark;
+import org.apache.geode.perftest.Task;
+import org.apache.geode.perftest.TestContext;
+
+public class InitRedisServersAttribute implements Task {
+  private static final Logger logger = LoggerFactory.getLogger(InitRedisServersAttribute.class);
+
+  final Collection<InetSocketAddress> servers;
+
+  public InitRedisServersAttribute() {
+    this(null);
+  }
+
+  public InitRedisServersAttribute(final Collection<InetSocketAddress> servers) {
+    this.servers = servers;
+  }
+
+  public Collection<InetSocketAddress> getServers() {
+    return servers;
+  }
+
+  @Override
+  public void run(final TestContext context) throws Exception {
+    if (null == servers) {
+      final List<InetSocketAddress> servers =
+          context.getHostsForRole(SERVER.name()).stream().map(i -> InetSocketAddress
+              .createUnresolved(i.getHostAddress(), REDIS_PORT)).collect(Collectors.toList());
+      logger.info("Setting servers from roles: {}", servers);
+      context.setAttribute(RedisBenchmark.REDIS_SERVERS_ATTRIBUTE, servers);
+    } else {
+      logger.info("Setting servers from fixed collection: {}", servers);
+      context.setAttribute(RedisBenchmark.REDIS_SERVERS_ATTRIBUTE, servers);
+    }
+  }
+
+}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/InitRegion.java
similarity index 65%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/InitRegion.java
index 6ffd348..66302d1 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/InitRegion.java
@@ -15,21 +15,25 @@
  * limitations under the License.
  */
 
-package org.apache.geode.benchmark.tasks;
+package org.apache.geode.benchmark.redis.tasks;
+
+
+import static org.apache.geode.benchmark.tasks.StartServer.SERVER_CACHE;
 
 import org.apache.geode.cache.Cache;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.partition.PartitionRegionHelper;
 import org.apache.geode.perftest.Task;
 import org.apache.geode.perftest.TestContext;
 
-/**
- * Task to stop the server.
- */
-public class StopServer implements Task {
+public class InitRegion implements Task {
+
+  public static final String REDIS_DATA_REGION = "__REDIS_DATA";
 
   @Override
   public void run(final TestContext context) throws Exception {
-    final Cache cache = (Cache) context.getAttribute("SERVER_CACHE");
-    cache.close();
+    final Cache cache = (Cache) context.getAttribute(SERVER_CACHE);
+    final Region<?, ?> region = cache.getRegion(REDIS_DATA_REGION);
+    PartitionRegionHelper.assignBucketsToPartitions(region);
   }
-
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/JedisClientManager.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/JedisClientManager.java
new file mode 100644
index 0000000..ffaae83
--- /dev/null
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/JedisClientManager.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.benchmark.redis.tasks;
+
+import static java.lang.Thread.currentThread;
+import static redis.clients.jedis.BinaryJedisCluster.HASHSLOTS;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import redis.clients.jedis.HostAndPort;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisCluster;
+import redis.clients.jedis.JedisPoolConfig;
+
+public final class JedisClientManager implements RedisClientManager {
+  private static final Logger logger = LoggerFactory.getLogger(RedisClientManager.class);
+
+  private static JedisCluster jedisCluster;
+
+  private static final RedisClient redisClient = new RedisClient() {
+    @Override
+    public String get(final String key) {
+      return jedisCluster.get(key);
+    }
+
+    @Override
+    public String set(final String key, final String value) {
+      return jedisCluster.set(key, value);
+    }
+
+    @Override
+    public String hget(final String key, final String field) {
+      return jedisCluster.hget(key, field);
+    }
+
+    @Override
+    public boolean hset(final String key, final String field, final String value) {
+      return 1 == jedisCluster.hset(key, field, value);
+    }
+
+    @Override
+    public void flushdb() {
+      Set<String> seen = new HashSet<>();
+      for (int i = 0; i < HASHSLOTS; ++i) {
+        try (final Jedis connectionFromSlot = jedisCluster.getConnectionFromSlot(i)) {
+          if (seen.add(connectionFromSlot.getClient().getHost())) {
+            logger.info("Executing flushdb on {}", connectionFromSlot.getClient().getHost());
+            connectionFromSlot.flushDB();
+          }
+        }
+      }
+    }
+  };
+
+  @Override
+  public void connect(final Collection<InetSocketAddress> servers) {
+    logger.info("Connect RedisClient on thread {}.", currentThread());
+
+    final Set<HostAndPort> nodes = servers.stream()
+        .map(i -> new HostAndPort(i.getHostString(), i.getPort())).collect(Collectors.toSet());
+
+    final JedisPoolConfig poolConfig = new JedisPoolConfig();
+    poolConfig.setMaxTotal(-1);
+    poolConfig.setMaxIdle(-1);
+    poolConfig.setLifo(false);
+    final JedisCluster jedisCluster = new JedisCluster(nodes, Integer.MAX_VALUE, poolConfig);
+
+    long start = System.nanoTime();
+    while (true) {
+      try (final Jedis jedis = jedisCluster.getConnectionFromSlot(0)) {
+        logger.info("Waiting for cluster to come up.");
+        final String clusterInfo = jedis.clusterInfo();
+        if (clusterInfo.contains("cluster_state:ok")) {
+          break;
+        }
+        logger.debug(clusterInfo);
+      } catch (Exception e) {
+        if (System.nanoTime() - start > CONNECT_TIMEOUT.toNanos()) {
+          throw e;
+        }
+        try {
+          Thread.sleep(50);
+        } catch (InterruptedException interruptedException) {
+          throw new RuntimeException(e);
+        }
+        logger.info("Failed connecting.", e);
+      }
+    }
+
+    JedisClientManager.jedisCluster = jedisCluster;
+  }
+
+  @Override
+  public void close() {
+    logger.info("Close RedisClient on thread {}.", currentThread());
+
+    jedisCluster.close();
+  }
+
+  @Override
+  public RedisClient get() {
+    logger.info("Getting RedisClient on thread {}.", currentThread());
+
+    return redisClient;
+  }
+}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/LettuceClientManager.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/LettuceClientManager.java
new file mode 100644
index 0000000..bd38216
--- /dev/null
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/LettuceClientManager.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.benchmark.redis.tasks;
+
+import static java.lang.Thread.currentThread;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import io.lettuce.core.ReadFrom;
+import io.lettuce.core.RedisURI;
+import io.lettuce.core.cluster.RedisClusterClient;
+import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
+import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class LettuceClientManager implements RedisClientManager {
+  private static final Logger logger = LoggerFactory.getLogger(LettuceClientManager.class);
+
+  private static RedisClusterClient redisClusterClient;
+
+  private static final ThreadLocal<RedisAdvancedClusterCommands<String, String>> redisAdvancedClusterCommands =
+      ThreadLocal.withInitial(() -> {
+        logger.info("Setup for thread {}", Thread.currentThread().getId());
+
+        final StatefulRedisClusterConnection<String, String> redisClusterConnection =
+            redisClusterClient.connect();
+        redisClusterConnection.setReadFrom(ReadFrom.ANY);
+        return redisClusterConnection.sync();
+      });
+
+
+  private static final RedisClient redisClient = new RedisClient() {
+    @Override
+    public String get(final String key) {
+      return redisAdvancedClusterCommands.get().get(key);
+    }
+
+    @Override
+    public String set(final String key, final String value) {
+      return redisAdvancedClusterCommands.get().set(key, value);
+    }
+
+    @Override
+    public String hget(final String key, final String field) {
+      return redisAdvancedClusterCommands.get().hget(key, field);
+    }
+
+    @Override
+    public boolean hset(final String key, final String field, final String value) {
+      return redisAdvancedClusterCommands.get().hset(key, field, value);
+    }
+
+    @Override
+    public void flushdb() {
+      redisAdvancedClusterCommands.get().flushdb();
+    }
+  };
+
+  @Override
+  public void connect(final Collection<InetSocketAddress> servers) {
+    logger.info("Connect RedisClient on thread {}.", currentThread());
+
+    final List<RedisURI> nodes = servers.stream()
+        .map(i -> RedisURI.create(i.getHostString(), i.getPort())).collect(Collectors.toList());
+
+    final RedisClusterClient redisClusterClient = RedisClusterClient.create(nodes);
+
+    long start = System.nanoTime();
+    while (true) {
+      try (final StatefulRedisClusterConnection<String, String> connection =
+          redisClusterClient.connect()) {
+        logger.info("Waiting for cluster to come up.");
+        final String clusterInfo = connection.sync().clusterInfo();
+        if (clusterInfo.contains("cluster_state:ok")) {
+          break;
+        }
+        logger.debug(clusterInfo);
+      } catch (Exception e) {
+        if (System.nanoTime() - start > CONNECT_TIMEOUT.toNanos()) {
+          throw e;
+        }
+        try {
+          Thread.sleep(50);
+        } catch (InterruptedException interruptedException) {
+          throw new RuntimeException(e);
+        }
+        logger.info("Failed connecting.", e);
+      }
+    }
+
+    redisClusterClient.refreshPartitions();
+
+    LettuceClientManager.redisClusterClient = redisClusterClient;
+  }
+
+  @Override
+  public void close() {
+    logger.info("Close RedisClient on thread {}.", currentThread());
+
+    redisClusterClient.shutdown();
+  }
+
+  @Override
+  public RedisClient get() {
+    logger.info("Getting RedisClient on thread {}.", currentThread());
+
+    return redisClient;
+  }
+}
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedPutBenchmarkTest.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/LongStringCache.java
similarity index 62%
copy from geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedPutBenchmarkTest.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/LongStringCache.java
index 157c48c..aecc5c1 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedPutBenchmarkTest.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/LongStringCache.java
@@ -15,27 +15,21 @@
  * limitations under the License.
  */
 
-package org.apache.geode.benchmark.tests;
-
-import java.io.File;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
+package org.apache.geode.benchmark.redis.tasks;
 
 import org.apache.geode.benchmark.LongRange;
-import org.apache.geode.perftest.TestRunners;
 
-public class ReplicatedPutBenchmarkTest {
+public class LongStringCache {
+  private final int offset;
+  private final String[] cache;
 
-  @TempDir
-  File folder;
+  public LongStringCache(final LongRange longRange) {
+    offset = (int) longRange.getMin();
+    cache = new String[(int) (longRange.getMax() - offset)];
+    longRange.forEach(i -> cache[(int) i] = String.valueOf(i));
+  }
 
-  @Test
-  public void benchmarkRunsSuccessfully()
-      throws Exception {
-    ReplicatedPutBenchmark test = new ReplicatedPutBenchmark();
-    test.setKeyRange(new LongRange(0, 100));
-    TestRunners.minimalRunner(folder)
-        .runTest(test);
+  public String valueOf(final long value) {
+    return cache[(int) (value - offset)];
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegionLong.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/PrePopulateRedis.java
similarity index 63%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegionLong.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/PrePopulateRedis.java
index 49f4725..455b3d0 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegionLong.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/PrePopulateRedis.java
@@ -15,27 +15,23 @@
  * limitations under the License.
  */
 
-package org.apache.geode.benchmark.tasks;
+package org.apache.geode.benchmark.redis.tasks;
 
-import org.apache.geode.benchmark.LongRange;
-import org.apache.geode.benchmark.topology.Roles;
+import static java.lang.String.valueOf;
 
-public class PrePopulateRegionLong extends AbstractPrePopulateRegion<Long> {
+import org.apache.geode.benchmark.LongRange;
 
-  public PrePopulateRegionLong() {}
+public class PrePopulateRedis extends AbstractPrePopulate {
 
-  public PrePopulateRegionLong(LongRange keyRangeToPrepopulate) {
-    super(keyRangeToPrepopulate);
-  }
-
-  public PrePopulateRegionLong(LongRange keyRangeToPrepopulate,
-      Roles targetRole) {
-    super(keyRangeToPrepopulate, targetRole);
+  public PrePopulateRedis(
+      final RedisClientManager redisClientManager,
+      final LongRange keyRangeToPrepopulate) {
+    super(redisClientManager, keyRangeToPrepopulate);
   }
 
   @Override
-  protected Long getValue(long i) {
-    return i;
+  protected void prepopulate(final RedisClient redisClient, final long key) {
+    final String keyString = valueOf(key);
+    redisClient.set(keyString, keyString);
   }
-
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegionLong.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/PrePopulateRedisHash.java
similarity index 56%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegionLong.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/PrePopulateRedisHash.java
index 49f4725..c36371d 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegionLong.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/PrePopulateRedisHash.java
@@ -15,27 +15,25 @@
  * limitations under the License.
  */
 
-package org.apache.geode.benchmark.tasks;
+package org.apache.geode.benchmark.redis.tasks;
 
-import org.apache.geode.benchmark.LongRange;
-import org.apache.geode.benchmark.topology.Roles;
+import static java.lang.String.valueOf;
+import static org.apache.geode.benchmark.redis.tasks.RedisHash.toField;
+import static org.apache.geode.benchmark.redis.tasks.RedisHash.toKey;
 
-public class PrePopulateRegionLong extends AbstractPrePopulateRegion<Long> {
+import org.apache.geode.benchmark.LongRange;
 
-  public PrePopulateRegionLong() {}
+public class PrePopulateRedisHash extends AbstractPrePopulate {
 
-  public PrePopulateRegionLong(LongRange keyRangeToPrepopulate) {
-    super(keyRangeToPrepopulate);
-  }
-
-  public PrePopulateRegionLong(LongRange keyRangeToPrepopulate,
-      Roles targetRole) {
-    super(keyRangeToPrepopulate, targetRole);
+  public PrePopulateRedisHash(
+      final RedisClientManager redisClientManager,
+      final LongRange keyRangeToPrepopulate) {
+    super(redisClientManager, keyRangeToPrepopulate);
   }
 
   @Override
-  protected Long getValue(long i) {
-    return i;
+  protected void prepopulate(final RedisClient redisClient, final long key) {
+    final String value = valueOf(toField(key));
+    redisClient.hset(valueOf(toKey(key)), value, value);
   }
-
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/RedisClient.java
similarity index 75%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/RedisClient.java
index 922ff51..6155536 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/RedisClient.java
@@ -13,8 +13,16 @@
  * the License.
  */
 
-package org.apache.geode.benchmark.parameters;
+package org.apache.geode.benchmark.redis.tasks;
 
-public enum GcImplementation {
-  CMS, G1, Z, Shenandoah;
+public interface RedisClient {
+  String get(String key);
+
+  String set(String key, String value);
+
+  String hget(String key, String field);
+
+  boolean hset(String key, String field, String value);
+
+  void flushdb();
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/RedisClientManager.java
similarity index 65%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/RedisClientManager.java
index 922ff51..fd30f34 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/RedisClientManager.java
@@ -13,8 +13,20 @@
  * the License.
  */
 
-package org.apache.geode.benchmark.parameters;
+package org.apache.geode.benchmark.redis.tasks;
+
+import java.io.Serializable;
+import java.net.InetSocketAddress;
+import java.time.Duration;
+import java.util.Collection;
+
+public interface RedisClientManager extends Serializable {
+  Duration CONNECT_TIMEOUT = Duration.ofMinutes(1);
+
+  void connect(final Collection<InetSocketAddress> servers) throws InterruptedException;
+
+  void close();
+
+  RedisClient get();
 
-public enum GcImplementation {
-  CMS, G1, Z, Shenandoah;
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/RedisHash.java
similarity index 73%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/RedisHash.java
index 922ff51..3aecb0b 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/RedisHash.java
@@ -13,8 +13,18 @@
  * the License.
  */
 
-package org.apache.geode.benchmark.parameters;
+package org.apache.geode.benchmark.redis.tasks;
 
-public enum GcImplementation {
-  CMS, G1, Z, Shenandoah;
+/**
+ * Split keyspace into Redis hash key and field parts.
+ */
+public class RedisHash {
+
+  public static long toKey(final long key) {
+    return key / 1000;
+  }
+
+  public static long toField(final long key) {
+    return key % 1000;
+  }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/SetRedisTask.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/SetRedisTask.java
new file mode 100644
index 0000000..af12fc6
--- /dev/null
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/SetRedisTask.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geode.benchmark.redis.tasks;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.yardstickframework.BenchmarkConfiguration;
+import org.yardstickframework.BenchmarkDriverAdapter;
+
+import org.apache.geode.benchmark.LongRange;
+
+public class SetRedisTask extends BenchmarkDriverAdapter implements Serializable {
+  private static final Logger logger = LoggerFactory.getLogger(SetRedisTask.class);
+
+  private final RedisClientManager redisClientManager;
+  private final LongRange keyRange;
+  private final boolean validate;
+
+  private LongStringCache keyCache;
+  private transient RedisClient redisClient;
+
+
+  public SetRedisTask(final RedisClientManager redisClientManager, final LongRange keyRange,
+      final boolean validate) {
+    logger.info("Initialized: keyRange={}, validate={}", keyRange, validate);
+    this.redisClientManager = redisClientManager;
+    this.keyRange = keyRange;
+    this.validate = validate;
+  }
+
+  @Override
+  public void setUp(final BenchmarkConfiguration cfg) throws Exception {
+    super.setUp(cfg);
+
+    keyCache = new LongStringCache(keyRange);
+    redisClient = redisClientManager.get();
+  }
+
+  @Override
+  public boolean test(final Map<Object, Object> ctx) throws Exception {
+    final String key = keyCache.valueOf(keyRange.random());
+    final String response = redisClient.set(key, key);
+    if (validate) {
+      assertThat(response).isEqualTo("OK");
+    }
+    return true;
+  }
+
+}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/StartGeodeServer.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/StartGeodeServer.java
new file mode 100644
index 0000000..f9dfab9
--- /dev/null
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/StartGeodeServer.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geode.benchmark.redis.tasks;
+
+
+import static java.lang.String.valueOf;
+
+import org.apache.geode.benchmark.tasks.StartServer;
+import org.apache.geode.cache.CacheFactory;
+import org.apache.geode.cache.server.CacheServer;
+import org.apache.geode.perftest.TestContext;
+
+public class StartGeodeServer extends StartServer {
+
+  private final int redisPort;
+
+  public StartGeodeServer(final int locatorPort, final int serverPort, final int redisPort) {
+    super(locatorPort, serverPort);
+    this.redisPort = redisPort;
+  }
+
+  @Override
+  protected CacheFactory configureCacheFactory(final CacheFactory cacheFactory,
+      final TestContext context)
+      throws Exception {
+
+    return super.configureCacheFactory(cacheFactory, context)
+        .set("compatible-with-redis-enabled", valueOf(true))
+        .set("compatible-with-redis-port", valueOf(redisPort));
+  }
+
+  @Override
+  protected CacheServer configureCacheServer(final CacheServer cacheServer,
+      final TestContext context) {
+    return null;
+  }
+}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/StartRedisClient.java
similarity index 53%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/StartRedisClient.java
index 6ffd348..147d1b7 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/StartRedisClient.java
@@ -15,21 +15,37 @@
  * limitations under the License.
  */
 
-package org.apache.geode.benchmark.tasks;
+package org.apache.geode.benchmark.redis.tasks;
+
+import static org.apache.geode.benchmark.redis.tests.RedisBenchmark.REDIS_SERVERS_ATTRIBUTE;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-import org.apache.geode.cache.Cache;
 import org.apache.geode.perftest.Task;
 import org.apache.geode.perftest.TestContext;
 
 /**
- * Task to stop the server.
+ * Task to create the client cache
  */
-public class StopServer implements Task {
+public class StartRedisClient implements Task {
+  private static final Logger logger = LoggerFactory.getLogger(StartRedisClient.class);
+
+  private final RedisClientManager redisClientManager;
+
+  public StartRedisClient(final RedisClientManager redisClientManager) {
+    this.redisClientManager = redisClientManager;
+  }
 
   @Override
   public void run(final TestContext context) throws Exception {
-    final Cache cache = (Cache) context.getAttribute("SERVER_CACHE");
-    cache.close();
+    @SuppressWarnings("unchecked")
+    final Collection<InetSocketAddress> redisClusterAddresses =
+        (Collection<InetSocketAddress>) context.getAttribute(REDIS_SERVERS_ATTRIBUTE);
+    redisClientManager.connect(redisClusterAddresses);
   }
 
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/StartRedisServer.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/StartRedisServer.java
new file mode 100644
index 0000000..dcf3e2b
--- /dev/null
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/StartRedisServer.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.geode.benchmark.redis.tasks;
+
+import static java.net.InetAddress.getLocalHost;
+import static org.apache.geode.benchmark.tasks.ProcessControl.runAndExpectZeroExit;
+import static org.apache.geode.benchmark.topology.Roles.SERVER;
+
+import java.net.InetAddress;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.geode.perftest.Task;
+import org.apache.geode.perftest.TestContext;
+
+public class StartRedisServer implements Task {
+
+  @Override
+  public void run(final TestContext context) throws Exception {
+    final Set<InetAddress> servers = context.getHostsForRole(SERVER.name());
+
+    final String redisNodes =
+        servers.stream().map(InetAddress::getHostAddress).collect(Collectors.joining(" "));
+
+    final ProcessBuilder processBuilder =
+        new ProcessBuilder().command("docker", "run", "-d", "--rm",
+            "-e", "ALLOW_EMPTY_PASSWORD=yes",
+            "-e", "REDIS_NODES=" + redisNodes,
+            "-e", "REDIS_CLUSTER_DYNAMIC_IPS=no",
+            "-e", "REDIS_CLUSTER_ANNOUNCE_IP=" + getLocalHost().getHostAddress(),
+            "-e", "REDIS_AOF_ENABLED=no",
+            "--name", "redis-cluster-node",
+            "--network", "host",
+            "bitnami/redis-cluster:latest");
+
+    runAndExpectZeroExit(processBuilder);
+  }
+
+}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/StopRedisClient.java
similarity index 71%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/StopRedisClient.java
index 6ffd348..8c55fdf 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/StopRedisClient.java
@@ -15,21 +15,23 @@
  * limitations under the License.
  */
 
-package org.apache.geode.benchmark.tasks;
+package org.apache.geode.benchmark.redis.tasks;
+
+
 
-import org.apache.geode.cache.Cache;
 import org.apache.geode.perftest.Task;
 import org.apache.geode.perftest.TestContext;
 
-/**
- * Task to stop the server.
- */
-public class StopServer implements Task {
+public class StopRedisClient implements Task {
+  final RedisClientManager redisClientManager;
+
+  public StopRedisClient(final RedisClientManager redisClientManager) {
+    this.redisClientManager = redisClientManager;
+  }
 
   @Override
-  public void run(final TestContext context) throws Exception {
-    final Cache cache = (Cache) context.getAttribute("SERVER_CACHE");
-    cache.close();
+  public void run(TestContext context) throws Exception {
+    redisClientManager.close();
   }
 
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/StopRedisServer.java
similarity index 79%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/StopRedisServer.java
index 6ffd348..ffe776a 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tasks/StopRedisServer.java
@@ -15,21 +15,16 @@
  * limitations under the License.
  */
 
-package org.apache.geode.benchmark.tasks;
+package org.apache.geode.benchmark.redis.tasks;
 
-import org.apache.geode.cache.Cache;
 import org.apache.geode.perftest.Task;
 import org.apache.geode.perftest.TestContext;
 
-/**
- * Task to stop the server.
- */
-public class StopServer implements Task {
+public class StopRedisServer implements Task {
 
   @Override
   public void run(final TestContext context) throws Exception {
-    final Cache cache = (Cache) context.getAttribute("SERVER_CACHE");
-    cache.close();
+    new ProcessBuilder().command("docker", "kill", "redis-cluster-node").start();
   }
 
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisBenchmark.java
new file mode 100644
index 0000000..79a2a71
--- /dev/null
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisBenchmark.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.benchmark.redis.tests;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static java.lang.Long.getLong;
+import static org.apache.geode.benchmark.Config.after;
+import static org.apache.geode.benchmark.Config.before;
+import static org.apache.geode.benchmark.redis.tests.RedisBenchmark.RedisClientImplementation.Jedis;
+import static org.apache.geode.benchmark.redis.tests.RedisBenchmark.RedisClusterImplementation.Geode;
+import static org.apache.geode.benchmark.redis.tests.RedisBenchmark.RedisClusterImplementation.Manual;
+import static org.apache.geode.benchmark.tests.GeodeBenchmark.WITH_MAX_KEY;
+import static org.apache.geode.benchmark.tests.GeodeBenchmark.WITH_MIN_KEY;
+import static org.apache.geode.benchmark.topology.Roles.CLIENT;
+
+import org.junit.jupiter.api.Test;
+
+import org.apache.geode.benchmark.LongRange;
+import org.apache.geode.benchmark.redis.tasks.FlushDbTask;
+import org.apache.geode.benchmark.redis.tasks.JedisClientManager;
+import org.apache.geode.benchmark.redis.tasks.LettuceClientManager;
+import org.apache.geode.benchmark.redis.tasks.RedisClientManager;
+import org.apache.geode.benchmark.redis.tasks.StartRedisClient;
+import org.apache.geode.benchmark.redis.tasks.StopRedisClient;
+import org.apache.geode.benchmark.redis.topology.GeodeTopology;
+import org.apache.geode.benchmark.redis.topology.ManualRedisTopology;
+import org.apache.geode.benchmark.redis.topology.RedisTopology;
+import org.apache.geode.benchmark.tests.AbstractPerformanceTest;
+import org.apache.geode.benchmark.tests.GeodeBenchmark;
+import org.apache.geode.perftest.TestConfig;
+import org.apache.geode.perftest.TestRunners;
+
+public class RedisBenchmark extends AbstractPerformanceTest {
+
+  public static final String WITH_REDIS_CLIENT_PROPERTY = "benchmark.withRedisClient";
+  public static final String WITH_REDIS_CLUSTER_PROPERTY = "benchmark.withRedisCluster";
+
+  public static final String REDIS_SERVERS_ATTRIBUTE = "RedisBenchmark.Servers";
+
+  public enum RedisClientImplementation {
+    Jedis,
+    Lettuce;
+
+    public static RedisClientImplementation valueOfIgnoreCase(final String name) {
+      for (RedisClientImplementation redisClientImplementation : RedisClientImplementation
+          .values()) {
+        if (redisClientImplementation.name().equalsIgnoreCase(name)) {
+          return redisClientImplementation;
+        }
+      }
+      throw new IllegalArgumentException("Unknown Redis client implementation: " + name);
+    }
+  }
+
+  public enum RedisClusterImplementation {
+    Geode,
+    Redis,
+    Manual;
+
+    public static RedisClusterImplementation valueOfIgnoreCase(final String name) {
+      for (RedisClusterImplementation redisClusterImplementation : RedisClusterImplementation
+          .values()) {
+        if (redisClusterImplementation.name().equalsIgnoreCase(name)) {
+          return redisClusterImplementation;
+        }
+      }
+      throw new IllegalArgumentException("Unknown Redis cluster implementation: " + name);
+    }
+  }
+
+  protected transient RedisClientManager redisClientManager;
+
+  protected LongRange keyRange =
+      new LongRange(getLong(WITH_MIN_KEY, 0), getLong(WITH_MAX_KEY, 1000000));
+
+  public void setKeyRange(final LongRange keyRange) {
+    this.keyRange = keyRange;
+  }
+
+  @Test
+  public void run() throws Exception {
+    TestRunners.defaultRunner().runTest(this);
+  }
+
+  @Override
+  public TestConfig configure() {
+    TestConfig config = GeodeBenchmark.createConfig();
+
+    switch (getRedisClusterImplementation()) {
+      case Redis:
+        RedisTopology.configure(config);
+        break;
+      case Geode:
+        GeodeTopology.configure(config);
+        break;
+      case Manual:
+        ManualRedisTopology.configure(config);
+        break;
+    }
+
+    switch (getRedisClientImplementation()) {
+      case Jedis:
+        redisClientManager = new JedisClientManager();
+        break;
+      case Lettuce:
+        redisClientManager = new LettuceClientManager();
+        break;
+    }
+
+    before(config, new StartRedisClient(redisClientManager), CLIENT);
+
+    if (Manual == getRedisClusterImplementation()) {
+      before(config, new FlushDbTask(redisClientManager), CLIENT);
+    }
+
+    after(config, new StopRedisClient(redisClientManager), CLIENT);
+
+    return config;
+  }
+
+  private RedisClientImplementation getRedisClientImplementation() {
+    final String sniProp = System.getProperty(WITH_REDIS_CLIENT_PROPERTY);
+    if (isNullOrEmpty(sniProp)) {
+      return Jedis;
+    }
+
+    return RedisClientImplementation.valueOfIgnoreCase(sniProp);
+  }
+
+  private RedisClusterImplementation getRedisClusterImplementation() {
+    final String sniProp = System.getProperty(WITH_REDIS_CLUSTER_PROPERTY);
+    if (isNullOrEmpty(sniProp)) {
+      return Geode;
+    }
+
+    return RedisClusterImplementation.valueOfIgnoreCase(sniProp);
+  }
+
+}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisGetBenchmark.java
similarity index 64%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisGetBenchmark.java
index dbc9a12..23963ec 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisGetBenchmark.java
@@ -15,35 +15,24 @@
  * limitations under the License.
  */
 
-package org.apache.geode.benchmark.tests;
-
+package org.apache.geode.benchmark.redis.tests;
 
+import static org.apache.geode.benchmark.Config.before;
 import static org.apache.geode.benchmark.Config.workload;
 import static org.apache.geode.benchmark.topology.Roles.CLIENT;
 
-import org.junit.jupiter.api.Test;
-
-import org.apache.geode.benchmark.tasks.NoopTask;
-import org.apache.geode.perftest.PerformanceTest;
+import org.apache.geode.benchmark.redis.tasks.GetRedisTask;
+import org.apache.geode.benchmark.redis.tasks.PrePopulateRedis;
 import org.apache.geode.perftest.TestConfig;
-import org.apache.geode.perftest.TestRunners;
 
-/**
- * Benchmark of gets on a partitioned region.
- */
-public class NoopBenchmark implements PerformanceTest {
-
-  @Test
-  public void run() throws Exception {
-    TestRunners.defaultRunner().runTest(this);
-  }
-
-  public NoopBenchmark() {}
+public class RedisGetBenchmark extends RedisBenchmark {
 
   @Override
   public TestConfig configure() {
-    TestConfig config = ClientServerBenchmark.createConfig();
-    workload(config, new NoopTask(), CLIENT);
+    final TestConfig config = super.configure();
+
+    before(config, new PrePopulateRedis(redisClientManager, keyRange), CLIENT);
+    workload(config, new GetRedisTask(redisClientManager, keyRange, isValidationEnabled()), CLIENT);
     return config;
 
   }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisHgetBenchmark.java
similarity index 64%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisHgetBenchmark.java
index dbc9a12..8f40a46 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisHgetBenchmark.java
@@ -15,35 +15,25 @@
  * limitations under the License.
  */
 
-package org.apache.geode.benchmark.tests;
-
+package org.apache.geode.benchmark.redis.tests;
 
+import static org.apache.geode.benchmark.Config.before;
 import static org.apache.geode.benchmark.Config.workload;
 import static org.apache.geode.benchmark.topology.Roles.CLIENT;
 
-import org.junit.jupiter.api.Test;
-
-import org.apache.geode.benchmark.tasks.NoopTask;
-import org.apache.geode.perftest.PerformanceTest;
+import org.apache.geode.benchmark.redis.tasks.HgetRedisTask;
+import org.apache.geode.benchmark.redis.tasks.PrePopulateRedisHash;
 import org.apache.geode.perftest.TestConfig;
-import org.apache.geode.perftest.TestRunners;
 
-/**
- * Benchmark of gets on a partitioned region.
- */
-public class NoopBenchmark implements PerformanceTest {
-
-  @Test
-  public void run() throws Exception {
-    TestRunners.defaultRunner().runTest(this);
-  }
-
-  public NoopBenchmark() {}
+public class RedisHgetBenchmark extends RedisBenchmark {
 
   @Override
   public TestConfig configure() {
-    TestConfig config = ClientServerBenchmark.createConfig();
-    workload(config, new NoopTask(), CLIENT);
+    final TestConfig config = super.configure();
+
+    before(config, new PrePopulateRedisHash(redisClientManager, keyRange), CLIENT);
+    workload(config, new HgetRedisTask(redisClientManager, keyRange, isValidationEnabled()),
+        CLIENT);
     return config;
 
   }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisHsetAndHgetBenchmark.java
similarity index 59%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisHsetAndHgetBenchmark.java
index dbc9a12..7d8f87e 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisHsetAndHgetBenchmark.java
@@ -15,36 +15,30 @@
  * limitations under the License.
  */
 
-package org.apache.geode.benchmark.tests;
-
+package org.apache.geode.benchmark.redis.tests;
 
+import static org.apache.geode.benchmark.Config.before;
 import static org.apache.geode.benchmark.Config.workload;
 import static org.apache.geode.benchmark.topology.Roles.CLIENT;
 
-import org.junit.jupiter.api.Test;
-
-import org.apache.geode.benchmark.tasks.NoopTask;
-import org.apache.geode.perftest.PerformanceTest;
+import org.apache.geode.benchmark.redis.tasks.HgetRedisTask;
+import org.apache.geode.benchmark.redis.tasks.HsetRedisTask;
+import org.apache.geode.benchmark.redis.tasks.PrePopulateRedisHash;
+import org.apache.geode.benchmark.tasks.CompositeTask;
 import org.apache.geode.perftest.TestConfig;
-import org.apache.geode.perftest.TestRunners;
-
-/**
- * Benchmark of gets on a partitioned region.
- */
-public class NoopBenchmark implements PerformanceTest {
-
-  @Test
-  public void run() throws Exception {
-    TestRunners.defaultRunner().runTest(this);
-  }
 
-  public NoopBenchmark() {}
+public class RedisHsetAndHgetBenchmark extends RedisBenchmark {
 
   @Override
   public TestConfig configure() {
-    TestConfig config = ClientServerBenchmark.createConfig();
-    workload(config, new NoopTask(), CLIENT);
-    return config;
+    final TestConfig config = super.configure();
+
+    before(config, new PrePopulateRedisHash(redisClientManager, keyRange), CLIENT);
 
+    workload(config, new CompositeTask(
+        new HsetRedisTask(redisClientManager, keyRange),
+        new HgetRedisTask(redisClientManager, keyRange, false)), CLIENT);
+
+    return config;
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisHsetBenchmark.java
similarity index 64%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisHsetBenchmark.java
index dbc9a12..0b59142 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisHsetBenchmark.java
@@ -15,35 +15,25 @@
  * limitations under the License.
  */
 
-package org.apache.geode.benchmark.tests;
-
+package org.apache.geode.benchmark.redis.tests;
 
+import static org.apache.geode.benchmark.Config.before;
 import static org.apache.geode.benchmark.Config.workload;
 import static org.apache.geode.benchmark.topology.Roles.CLIENT;
 
-import org.junit.jupiter.api.Test;
-
-import org.apache.geode.benchmark.tasks.NoopTask;
-import org.apache.geode.perftest.PerformanceTest;
+import org.apache.geode.benchmark.redis.tasks.HsetRedisTask;
+import org.apache.geode.benchmark.redis.tasks.PrePopulateRedisHash;
 import org.apache.geode.perftest.TestConfig;
-import org.apache.geode.perftest.TestRunners;
 
-/**
- * Benchmark of gets on a partitioned region.
- */
-public class NoopBenchmark implements PerformanceTest {
-
-  @Test
-  public void run() throws Exception {
-    TestRunners.defaultRunner().runTest(this);
-  }
-
-  public NoopBenchmark() {}
+public class RedisHsetBenchmark extends RedisBenchmark {
 
   @Override
   public TestConfig configure() {
-    TestConfig config = ClientServerBenchmark.createConfig();
-    workload(config, new NoopTask(), CLIENT);
+    final TestConfig config = super.configure();
+
+    before(config, new PrePopulateRedisHash(redisClientManager, keyRange), CLIENT);
+    workload(config, new HsetRedisTask(redisClientManager, keyRange),
+        CLIENT);
     return config;
 
   }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisSetBenchmark.java
similarity index 64%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisSetBenchmark.java
index dbc9a12..a5e4eff 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisSetBenchmark.java
@@ -15,35 +15,24 @@
  * limitations under the License.
  */
 
-package org.apache.geode.benchmark.tests;
-
+package org.apache.geode.benchmark.redis.tests;
 
+import static org.apache.geode.benchmark.Config.before;
 import static org.apache.geode.benchmark.Config.workload;
 import static org.apache.geode.benchmark.topology.Roles.CLIENT;
 
-import org.junit.jupiter.api.Test;
-
-import org.apache.geode.benchmark.tasks.NoopTask;
-import org.apache.geode.perftest.PerformanceTest;
+import org.apache.geode.benchmark.redis.tasks.PrePopulateRedis;
+import org.apache.geode.benchmark.redis.tasks.SetRedisTask;
 import org.apache.geode.perftest.TestConfig;
-import org.apache.geode.perftest.TestRunners;
 
-/**
- * Benchmark of gets on a partitioned region.
- */
-public class NoopBenchmark implements PerformanceTest {
-
-  @Test
-  public void run() throws Exception {
-    TestRunners.defaultRunner().runTest(this);
-  }
-
-  public NoopBenchmark() {}
+public class RedisSetBenchmark extends RedisBenchmark {
 
   @Override
   public TestConfig configure() {
-    TestConfig config = ClientServerBenchmark.createConfig();
-    workload(config, new NoopTask(), CLIENT);
+    final TestConfig config = super.configure();
+
+    before(config, new PrePopulateRedis(redisClientManager, keyRange), CLIENT);
+    workload(config, new SetRedisTask(redisClientManager, keyRange, isValidationEnabled()), CLIENT);
     return config;
 
   }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisWeightedHsetAndHgetBenchmark.java
similarity index 55%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisWeightedHsetAndHgetBenchmark.java
index dbc9a12..635a0b1 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/tests/RedisWeightedHsetAndHgetBenchmark.java
@@ -15,36 +15,31 @@
  * limitations under the License.
  */
 
-package org.apache.geode.benchmark.tests;
-
+package org.apache.geode.benchmark.redis.tests;
 
+import static org.apache.geode.benchmark.Config.before;
 import static org.apache.geode.benchmark.Config.workload;
 import static org.apache.geode.benchmark.topology.Roles.CLIENT;
 
-import org.junit.jupiter.api.Test;
-
-import org.apache.geode.benchmark.tasks.NoopTask;
-import org.apache.geode.perftest.PerformanceTest;
+import org.apache.geode.benchmark.redis.tasks.HgetRedisTask;
+import org.apache.geode.benchmark.redis.tasks.HsetRedisTask;
+import org.apache.geode.benchmark.redis.tasks.PrePopulateRedisHash;
+import org.apache.geode.benchmark.tasks.WeightedTasks;
+import org.apache.geode.benchmark.tasks.WeightedTasks.WeightedTask;
 import org.apache.geode.perftest.TestConfig;
-import org.apache.geode.perftest.TestRunners;
-
-/**
- * Benchmark of gets on a partitioned region.
- */
-public class NoopBenchmark implements PerformanceTest {
-
-  @Test
-  public void run() throws Exception {
-    TestRunners.defaultRunner().runTest(this);
-  }
 
-  public NoopBenchmark() {}
+public class RedisWeightedHsetAndHgetBenchmark extends RedisBenchmark {
 
   @Override
   public TestConfig configure() {
-    TestConfig config = ClientServerBenchmark.createConfig();
-    workload(config, new NoopTask(), CLIENT);
-    return config;
+    final TestConfig config = super.configure();
+
+    before(config, new PrePopulateRedisHash(redisClientManager, keyRange), CLIENT);
 
+    workload(config, new WeightedTasks(
+        new WeightedTask(20, new HsetRedisTask(redisClientManager, keyRange)),
+        new WeightedTask(80, new HgetRedisTask(redisClientManager, keyRange, false))), CLIENT);
+
+    return config;
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopology.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/topology/GeodeTopology.java
similarity index 62%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopology.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/topology/GeodeTopology.java
index 3678596..2df3097 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopology.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/topology/GeodeTopology.java
@@ -12,29 +12,34 @@
  * or implied. See the License for the specific language governing permissions and limitations under
  * the License.
  */
-package org.apache.geode.benchmark.topology;
+
+package org.apache.geode.benchmark.redis.topology;
 
 import static org.apache.geode.benchmark.Config.after;
 import static org.apache.geode.benchmark.Config.before;
 import static org.apache.geode.benchmark.Config.role;
 import static org.apache.geode.benchmark.topology.Ports.EPHEMERAL_PORT;
 import static org.apache.geode.benchmark.topology.Ports.LOCATOR_PORT;
+import static org.apache.geode.benchmark.topology.Ports.REDIS_PORT;
 import static org.apache.geode.benchmark.topology.Roles.CLIENT;
 import static org.apache.geode.benchmark.topology.Roles.LOCATOR;
 import static org.apache.geode.benchmark.topology.Roles.SERVER;
 
-import org.apache.geode.benchmark.tasks.StartClient;
+import org.apache.geode.benchmark.redis.parameters.GeodeParameters;
+import org.apache.geode.benchmark.redis.parameters.NettyParameters;
+import org.apache.geode.benchmark.redis.tasks.InitRedisServersAttribute;
+import org.apache.geode.benchmark.redis.tasks.InitRegion;
+import org.apache.geode.benchmark.redis.tasks.StartGeodeServer;
 import org.apache.geode.benchmark.tasks.StartLocator;
-import org.apache.geode.benchmark.tasks.StartServer;
-import org.apache.geode.benchmark.tasks.StopClient;
 import org.apache.geode.benchmark.tasks.StopLocator;
 import org.apache.geode.benchmark.tasks.StopServer;
+import org.apache.geode.benchmark.topology.Topology;
 import org.apache.geode.perftest.TestConfig;
 
-public class ClientServerTopology extends Topology {
-  private static final int NUM_LOCATORS = 1;
-  private static final int NUM_SERVERS = 2;
-  private static final int NUM_CLIENTS = 1;
+public class GeodeTopology extends Topology {
+  private static final int NUM_LOCATORS = Integer.getInteger(WITH_LOCATOR_COUNT_PROPERTY, 1);
+  private static final int NUM_SERVERS = Integer.getInteger(WITH_SERVER_COUNT_PROPERTY, 6);
+  private static final int NUM_CLIENTS = Integer.getInteger(WITH_CLIENT_COUNT_PROPERTY, 1);
 
   public static void configure(TestConfig config) {
     role(config, LOCATOR, NUM_LOCATORS);
@@ -43,11 +48,14 @@ public class ClientServerTopology extends Topology {
 
     configureCommon(config);
 
+    NettyParameters.configure(config);
+    GeodeParameters.configure(config);
+
     before(config, new StartLocator(LOCATOR_PORT), LOCATOR);
-    before(config, new StartServer(LOCATOR_PORT, EPHEMERAL_PORT), SERVER);
-    before(config, new StartClient(LOCATOR_PORT), CLIENT);
+    before(config, new StartGeodeServer(LOCATOR_PORT, EPHEMERAL_PORT, REDIS_PORT), SERVER);
+    before(config, new InitRegion(), SERVER);
+    before(config, new InitRedisServersAttribute(), CLIENT);
 
-    after(config, new StopClient(), CLIENT);
     after(config, new StopServer(), SERVER);
     after(config, new StopLocator(), LOCATOR);
   }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/topology/ManualRedisTopology.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/topology/ManualRedisTopology.java
new file mode 100644
index 0000000..2074d74
--- /dev/null
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/topology/ManualRedisTopology.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.benchmark.redis.topology;
+
+import static java.lang.Integer.parseInt;
+import static java.util.Arrays.stream;
+import static org.apache.geode.benchmark.Config.before;
+import static org.apache.geode.benchmark.Config.role;
+import static org.apache.geode.benchmark.topology.Ports.REDIS_PORT;
+import static org.apache.geode.benchmark.topology.Roles.CLIENT;
+
+import java.net.InetSocketAddress;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.geode.benchmark.redis.tasks.InitRedisServersAttribute;
+import org.apache.geode.benchmark.topology.Topology;
+import org.apache.geode.perftest.TestConfig;
+
+public class ManualRedisTopology extends Topology {
+  private static final int NUM_CLIENTS = Integer.getInteger(WITH_CLIENT_COUNT_PROPERTY, 1);
+
+  public static final String WITH_REDIS_SERVERS_PROPERTY = "benchmark.withRedisServers";
+
+  public static void configure(TestConfig config) {
+    role(config, CLIENT, NUM_CLIENTS);
+
+    configureCommon(config);
+
+    // Elasticache DNS is flaky so don't cache any of it.
+    config.jvmArgs(CLIENT.name(), "-Dsun.net.inetaddr.ttl=0", "-Dsun.net.inetaddr.negative.ttl=0");
+
+    final String serversProperty = System.getProperty(WITH_REDIS_SERVERS_PROPERTY);
+    if (null == serversProperty || serversProperty.trim().isEmpty()) {
+      throw new IllegalArgumentException(
+          WITH_REDIS_SERVERS_PROPERTY + " must be set to server address(es).");
+    }
+
+    final List<InetSocketAddress> servers = stream(serversProperty.split(";")).map(s -> {
+      final String[] addressParts = s.split(":");
+      return InetSocketAddress.createUnresolved(addressParts[0],
+          addressParts.length == 1 ? REDIS_PORT : parseInt(addressParts[1]));
+    }).collect(Collectors.toList());
+
+    before(config, new InitRedisServersAttribute(servers), CLIENT);
+  }
+}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopology.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/topology/RedisTopology.java
similarity index 53%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopology.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/topology/RedisTopology.java
index 3678596..4c18fd0 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopology.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/redis/topology/RedisTopology.java
@@ -12,29 +12,34 @@
  * or implied. See the License for the specific language governing permissions and limitations under
  * the License.
  */
-package org.apache.geode.benchmark.topology;
 
+package org.apache.geode.benchmark.redis.topology;
+
+import static java.lang.Integer.getInteger;
 import static org.apache.geode.benchmark.Config.after;
 import static org.apache.geode.benchmark.Config.before;
 import static org.apache.geode.benchmark.Config.role;
-import static org.apache.geode.benchmark.topology.Ports.EPHEMERAL_PORT;
-import static org.apache.geode.benchmark.topology.Ports.LOCATOR_PORT;
+import static org.apache.geode.benchmark.tests.GeodeBenchmark.WITH_REPLICAS;
 import static org.apache.geode.benchmark.topology.Roles.CLIENT;
 import static org.apache.geode.benchmark.topology.Roles.LOCATOR;
 import static org.apache.geode.benchmark.topology.Roles.SERVER;
 
-import org.apache.geode.benchmark.tasks.StartClient;
-import org.apache.geode.benchmark.tasks.StartLocator;
-import org.apache.geode.benchmark.tasks.StartServer;
-import org.apache.geode.benchmark.tasks.StopClient;
-import org.apache.geode.benchmark.tasks.StopLocator;
-import org.apache.geode.benchmark.tasks.StopServer;
+import org.apache.geode.benchmark.redis.tasks.CreateRedisCluster;
+import org.apache.geode.benchmark.redis.tasks.InitRedisServersAttribute;
+import org.apache.geode.benchmark.redis.tasks.StartRedisServer;
+import org.apache.geode.benchmark.redis.tasks.StopRedisServer;
+import org.apache.geode.benchmark.topology.Topology;
 import org.apache.geode.perftest.TestConfig;
 
-public class ClientServerTopology extends Topology {
-  private static final int NUM_LOCATORS = 1;
-  private static final int NUM_SERVERS = 2;
-  private static final int NUM_CLIENTS = 1;
+/**
+ * Redis running in containers on the provided servers.
+ *
+ * Locators hosts are wasted so that Redis server placement happens on the same hosts as Geode.
+ */
+public class RedisTopology extends Topology {
+  private static final int NUM_LOCATORS = Integer.getInteger(WITH_LOCATOR_COUNT_PROPERTY, 1);
+  private static final int NUM_SERVERS = Integer.getInteger(WITH_SERVER_COUNT_PROPERTY, 6);
+  private static final int NUM_CLIENTS = Integer.getInteger(WITH_CLIENT_COUNT_PROPERTY, 1);
 
   public static void configure(TestConfig config) {
     role(config, LOCATOR, NUM_LOCATORS);
@@ -43,12 +48,10 @@ public class ClientServerTopology extends Topology {
 
     configureCommon(config);
 
-    before(config, new StartLocator(LOCATOR_PORT), LOCATOR);
-    before(config, new StartServer(LOCATOR_PORT, EPHEMERAL_PORT), SERVER);
-    before(config, new StartClient(LOCATOR_PORT), CLIENT);
+    before(config, new StartRedisServer(), SERVER);
+    before(config, new CreateRedisCluster(getInteger(WITH_REPLICAS, 1)), CLIENT);
+    before(config, new InitRedisServersAttribute(), CLIENT);
 
-    after(config, new StopClient(), CLIENT);
-    after(config, new StopServer(), SERVER);
-    after(config, new StopLocator(), LOCATOR);
+    after(config, new StopRedisServer(), SERVER);
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/AbstractPrePopulateRegion.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/AbstractPrePopulateRegion.java
index f88da40..a1df171 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/AbstractPrePopulateRegion.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/AbstractPrePopulateRegion.java
@@ -41,7 +41,7 @@ import org.apache.geode.perftest.Task;
 import org.apache.geode.perftest.TestContext;
 
 
-public abstract class AbstractPrePopulateRegion<V> implements Task {
+public abstract class AbstractPrePopulateRegion<K, V> implements Task {
   private static final Logger logger = LoggerFactory.getLogger(AbstractPrePopulateRegion.class);
 
   private final LongRange keyRangeToPrepopulate;
@@ -68,7 +68,7 @@ public abstract class AbstractPrePopulateRegion<V> implements Task {
   @Override
   public void run(TestContext context) throws InterruptedException {
     final Cache cache = CacheFactory.getAnyInstance();
-    final Region<Long, V> region = cache.getRegion("region");
+    final Region<K, V> region = cache.getRegion("region");
     final ArrayList<Integer> hostIds =
         new ArrayList<>(context.getHostsIDsForRole(targetRole.name()));
 
@@ -76,7 +76,7 @@ public abstract class AbstractPrePopulateRegion<V> implements Task {
         keyRangeToPrepopulate.sliceFor(hostIds.size(), hostIds.indexOf(context.getJvmID())));
   }
 
-  void run(final Map<Long, V> region, final LongRange range) throws InterruptedException {
+  void run(final Map<K, V> region, final LongRange range) throws InterruptedException {
     logger.info("*******************************************");
     logger.info("      Prepopulating the region ");
     logger.info("*******************************************");
@@ -102,14 +102,16 @@ public abstract class AbstractPrePopulateRegion<V> implements Task {
     threadPool.awaitTermination(5, TimeUnit.MINUTES);
   }
 
-  private void doPuts(final Map<Long, V> region, final LongRange range) {
+  private void doPuts(final Map<K, V> region, final LongRange range) {
     for (final LongRange slice : range.slicesOfSize(batchSize)) {
-      final Map<Long, V> valueMap = new HashMap<>();
-      slice.forEach(i -> valueMap.put(i, getValue(i)));
+      final Map<K, V> valueMap = new HashMap<>();
+      slice.forEach(i -> valueMap.put(getKey(i), getValue(i)));
       region.putAll(valueMap);
     }
   }
 
+  protected abstract K getKey(long i);
+
   protected abstract V getValue(long i);
 
   public int getBatchSize() {
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/CompositeTask.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/CompositeTask.java
new file mode 100644
index 0000000..6cba205
--- /dev/null
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/CompositeTask.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.benchmark.tasks;
+
+import java.io.Serializable;
+import java.util.Map;
+
+import org.yardstickframework.BenchmarkConfiguration;
+import org.yardstickframework.BenchmarkDriver;
+
+public class CompositeTask implements BenchmarkDriver, Serializable {
+
+  private final BenchmarkDriver[] benchmarkDrivers;
+
+  public CompositeTask(BenchmarkDriver... benchmarkDrivers) {
+    this.benchmarkDrivers = benchmarkDrivers;
+  }
+
+
+  @Override
+  public void setUp(final BenchmarkConfiguration benchmarkConfiguration) throws Exception {
+    for (final BenchmarkDriver benchmarkDriver : benchmarkDrivers) {
+      benchmarkDriver.setUp(benchmarkConfiguration);
+    }
+  }
+
+  @Override
+  public boolean test(final Map<Object, Object> context) throws Exception {
+    for (final BenchmarkDriver benchmarkDriver : benchmarkDrivers) {
+      if (!benchmarkDriver.test(context)) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  @Override
+  public void tearDown() throws Exception {
+    for (final BenchmarkDriver benchmarkDriver : benchmarkDrivers) {
+      benchmarkDriver.tearDown();
+    }
+  }
+
+  @Override
+  public String description() {
+    final StringBuilder stringBuilder = new StringBuilder("Composite Task:\n");
+    for (final BenchmarkDriver benchmarkDriver : benchmarkDrivers) {
+      stringBuilder.append(benchmarkDriver.description()).append("\n");
+    }
+    return stringBuilder.toString();
+  }
+
+  @Override
+  public String usage() {
+    final StringBuilder stringBuilder = new StringBuilder("Composite Task:\n");
+    for (final BenchmarkDriver benchmarkDriver : benchmarkDrivers) {
+      stringBuilder.append(benchmarkDriver.usage()).append("\n");
+    }
+    return stringBuilder.toString();
+  }
+
+  @Override
+  public void onWarmupFinished() {
+    for (final BenchmarkDriver benchmarkDriver : benchmarkDrivers) {
+      benchmarkDriver.onWarmupFinished();
+    }
+  }
+
+  @Override
+  public void onException(final Throwable e) {
+    for (final BenchmarkDriver benchmarkDriver : benchmarkDrivers) {
+      benchmarkDriver.onException(e);
+    }
+  }
+}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/CreateIndexOnID.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/CreateIndexOnID.java
index 0201898..d215dd9 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/CreateIndexOnID.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/CreateIndexOnID.java
@@ -15,6 +15,8 @@
 package org.apache.geode.benchmark.tasks;
 
 
+import static org.apache.geode.benchmark.tasks.StartServer.SERVER_CACHE;
+
 import org.apache.geode.cache.Cache;
 import org.apache.geode.cache.query.IndexNameConflictException;
 import org.apache.geode.perftest.Task;
@@ -24,7 +26,7 @@ public class CreateIndexOnID implements Task {
 
   @Override
   public void run(TestContext context) throws Exception {
-    Cache serverCache = (Cache) context.getAttribute("SERVER_CACHE");
+    Cache serverCache = (Cache) context.getAttribute(SERVER_CACHE);
     try {
       serverCache.getQueryService().createIndex("IDIndex", "r.ID", "/region r");
     } catch (IndexNameConflictException ex) {
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/CreatePartitionedRegion.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/CreatePartitionedRegion.java
index 5f06860..ede80aa 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/CreatePartitionedRegion.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/CreatePartitionedRegion.java
@@ -15,6 +15,8 @@
 package org.apache.geode.benchmark.tasks;
 
 
+import static org.apache.geode.benchmark.tasks.StartServer.SERVER_CACHE;
+
 import org.apache.geode.cache.Cache;
 import org.apache.geode.cache.RegionShortcut;
 import org.apache.geode.perftest.Task;
@@ -27,7 +29,7 @@ public class CreatePartitionedRegion implements Task {
 
   @Override
   public void run(TestContext context) throws Exception {
-    Cache cache = (Cache) context.getAttribute("SERVER_CACHE");
+    final Cache cache = (Cache) context.getAttribute(SERVER_CACHE);
     cache.createRegionFactory(RegionShortcut.PARTITION_REDUNDANT).create("region");
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/CreateReplicatedRegion.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/CreateReplicatedRegion.java
index 51dbcd8..c06cfef 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/CreateReplicatedRegion.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/CreateReplicatedRegion.java
@@ -14,6 +14,8 @@
  */
 package org.apache.geode.benchmark.tasks;
 
+import static org.apache.geode.benchmark.tasks.StartServer.SERVER_CACHE;
+
 import org.apache.geode.cache.Cache;
 import org.apache.geode.cache.RegionShortcut;
 import org.apache.geode.perftest.Task;
@@ -25,7 +27,7 @@ import org.apache.geode.perftest.TestContext;
 public class CreateReplicatedRegion implements Task {
   @Override
   public void run(TestContext context) throws Exception {
-    Cache cache = (Cache) context.getAttribute("SERVER_CACHE");
+    Cache cache = (Cache) context.getAttribute(SERVER_CACHE);
     cache.createRegionFactory(RegionShortcut.REPLICATE).create("region");
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/ExecuteFilteredFunction.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/ExecuteFilteredFunction.java
index 610548d..bac845a 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/ExecuteFilteredFunction.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/ExecuteFilteredFunction.java
@@ -14,12 +14,15 @@
  */
 package org.apache.geode.benchmark.tasks;
 
+import static org.assertj.core.api.Assertions.assertThat;
+
 import java.io.Serializable;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 
 import benchmark.geode.data.FunctionWithFilter;
+import benchmark.geode.data.Portfolio;
 import org.yardstickframework.BenchmarkConfiguration;
 import org.yardstickframework.BenchmarkDriverAdapter;
 
@@ -29,17 +32,18 @@ import org.apache.geode.cache.client.ClientCache;
 import org.apache.geode.cache.client.ClientCacheFactory;
 import org.apache.geode.cache.execute.Function;
 import org.apache.geode.cache.execute.FunctionService;
-import org.apache.geode.cache.execute.ResultCollector;
 
 public class ExecuteFilteredFunction extends BenchmarkDriverAdapter implements Serializable {
 
   private final LongRange keyRange;
-  private final Function function;
+  private final boolean isValidationEnabled;
+  private final Function<Long> function;
 
-  private Region region;
+  private Region<Long, Portfolio> region;
 
-  public ExecuteFilteredFunction(final LongRange keyRange) {
+  public ExecuteFilteredFunction(final LongRange keyRange, final boolean isValidationEnabled) {
     this.keyRange = keyRange;
+    this.isValidationEnabled = isValidationEnabled;
     this.function = new FunctionWithFilter();
   }
 
@@ -55,13 +59,17 @@ public class ExecuteFilteredFunction extends BenchmarkDriverAdapter implements S
   public boolean test(Map<Object, Object> ctx) {
     final Set<Long> filterSet = Collections.singleton(keyRange.random());
     @SuppressWarnings("unchecked")
-    final ResultCollector<?, ?> resultCollector = FunctionService
+    final Object result = FunctionService
         .onRegion(region)
         .withFilter(filterSet)
-        .execute(function);
-    resultCollector.getResult();
-    return true;
+        .execute(function)
+        .getResult();
 
+    if (isValidationEnabled) {
+      assertThat(result).isInstanceOf(Portfolio.class);
+    }
+
+    return true;
   }
 
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/ExecuteFunction.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/ExecuteFunction.java
index 51c30de..b259005 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/ExecuteFunction.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/ExecuteFunction.java
@@ -14,10 +14,13 @@
  */
 package org.apache.geode.benchmark.tasks;
 
+import static org.assertj.core.api.Assertions.assertThat;
+
 import java.io.Serializable;
 import java.util.Map;
 
 import benchmark.geode.data.BenchmarkFunction;
+import benchmark.geode.data.Portfolio;
 import org.yardstickframework.BenchmarkConfiguration;
 import org.yardstickframework.BenchmarkDriverAdapter;
 
@@ -28,9 +31,13 @@ import org.apache.geode.cache.execute.FunctionService;
 
 public class ExecuteFunction extends BenchmarkDriverAdapter implements Serializable {
 
-  private Region region;
+  private final boolean isValidationEnabled;
+
+  private Region<Long, Portfolio> region;
 
-  public ExecuteFunction() {}
+  public ExecuteFunction(final boolean isValidationEnabled) {
+    this.isValidationEnabled = isValidationEnabled;
+  }
 
   @Override
   public void setUp(BenchmarkConfiguration cfg) throws Exception {
@@ -41,7 +48,13 @@ public class ExecuteFunction extends BenchmarkDriverAdapter implements Serializa
 
   @Override
   public boolean test(final Map<Object, Object> ctx) {
-    FunctionService.onRegion(region).execute(BenchmarkFunction.class.getName()).getResult();
+    final Object result =
+        FunctionService.onRegion(region).execute(BenchmarkFunction.class.getName()).getResult();
+
+    if (isValidationEnabled) {
+      assertThat(result).isInstanceOf(Portfolio.class);
+    }
+
     return true;
   }
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/ExecuteParameterizedFunction.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/ExecuteParameterizedFunction.java
index 6065b18..5d61e1e 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/ExecuteParameterizedFunction.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/ExecuteParameterizedFunction.java
@@ -14,10 +14,13 @@
  */
 package org.apache.geode.benchmark.tasks;
 
+import static org.assertj.core.api.Assertions.assertThat;
+
 import java.io.Serializable;
 import java.util.Map;
 
 import benchmark.geode.data.FunctionWithArguments;
+import benchmark.geode.data.Portfolio;
 import org.yardstickframework.BenchmarkConfiguration;
 import org.yardstickframework.BenchmarkDriverAdapter;
 
@@ -27,17 +30,18 @@ import org.apache.geode.cache.client.ClientCache;
 import org.apache.geode.cache.client.ClientCacheFactory;
 import org.apache.geode.cache.execute.Function;
 import org.apache.geode.cache.execute.FunctionService;
-import org.apache.geode.cache.execute.ResultCollector;
 
 public class ExecuteParameterizedFunction extends BenchmarkDriverAdapter implements Serializable {
 
   private final LongRange keyRange;
-  private final Function function;
+  private final boolean isValidationEnabled;
+  private final Function<Long> function;
 
-  private Region region;
+  private Region<Long, Portfolio> region;
 
-  public ExecuteParameterizedFunction(final LongRange keyRange) {
+  public ExecuteParameterizedFunction(final LongRange keyRange, final boolean isValidationEnabled) {
     this.keyRange = keyRange;
+    this.isValidationEnabled = isValidationEnabled;
     function = new FunctionWithArguments();
   }
 
@@ -52,11 +56,16 @@ public class ExecuteParameterizedFunction extends BenchmarkDriverAdapter impleme
   @Override
   public boolean test(Map<Object, Object> ctx) {
     @SuppressWarnings("unchecked")
-    ResultCollector<?, ?> resultCollector = FunctionService
+    final Object result = FunctionService
         .onRegion(region)
         .setArguments(keyRange.random())
-        .execute(function);
-    resultCollector.getResult();
+        .execute(function)
+        .getResult();
+
+    if (isValidationEnabled) {
+      assertThat(result).isInstanceOf(Portfolio.class);
+    }
+
     return true;
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/GetTask.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/GetStringTask.java
similarity index 69%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/GetTask.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/GetStringTask.java
index f6d701d..1de5fd5 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/GetTask.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/GetStringTask.java
@@ -17,6 +17,9 @@
 
 package org.apache.geode.benchmark.tasks;
 
+import static java.lang.String.valueOf;
+import static org.assertj.core.api.Assertions.assertThat;
+
 import java.io.Serializable;
 import java.util.Map;
 
@@ -32,14 +35,18 @@ import org.apache.geode.cache.Region;
  * Task workload to perform get operations on keys within 0
  * and the keyRange (exclusive)
  */
-public class GetTask extends BenchmarkDriverAdapter implements Serializable {
+public class GetStringTask extends BenchmarkDriverAdapter implements Serializable {
 
   private final LongRange keyRange;
+  private final boolean validate;
 
-  private Region<Object, Object> region;
+  private Region<String, String> region;
+  private long offset;
+  private String[] keys;
 
-  public GetTask(LongRange keyRange) {
+  public GetStringTask(final LongRange keyRange, final boolean validate) {
     this.keyRange = keyRange;
+    this.validate = validate;
   }
 
   @Override
@@ -48,12 +55,19 @@ public class GetTask extends BenchmarkDriverAdapter implements Serializable {
 
     final Cache cache = CacheFactory.getAnyInstance();
     region = cache.getRegion("region");
+
+    offset = keyRange.getMin();
+    keys = new String[(int) (keyRange.getMax() - offset)];
+    keyRange.forEach(i -> keys[(int) i] = valueOf(i));
   }
 
   @Override
   public boolean test(Map<Object, Object> ctx) throws Exception {
-    final long key = keyRange.random();
-    region.get(key);
+    final String key = keys[(int) (keyRange.random() - offset)];
+    final String value = region.get(key);
+    if (validate) {
+      assertThat(value).isEqualTo(key);
+    }
     return true;
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/GetTask.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/GetTask.java
index f6d701d..64c89a5 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/GetTask.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/GetTask.java
@@ -17,6 +17,8 @@
 
 package org.apache.geode.benchmark.tasks;
 
+import static org.assertj.core.api.Assertions.assertThat;
+
 import java.io.Serializable;
 import java.util.Map;
 
@@ -35,11 +37,13 @@ import org.apache.geode.cache.Region;
 public class GetTask extends BenchmarkDriverAdapter implements Serializable {
 
   private final LongRange keyRange;
+  private final boolean isValidationEnabled;
 
-  private Region<Object, Object> region;
+  private Region<Long, Object> region;
 
-  public GetTask(LongRange keyRange) {
+  public GetTask(LongRange keyRange, final boolean isValidationEnabled) {
     this.keyRange = keyRange;
+    this.isValidationEnabled = isValidationEnabled;
   }
 
   @Override
@@ -53,7 +57,12 @@ public class GetTask extends BenchmarkDriverAdapter implements Serializable {
   @Override
   public boolean test(Map<Object, Object> ctx) throws Exception {
     final long key = keyRange.random();
-    region.get(key);
+    final Object result = region.get(key);
+
+    if (isValidationEnabled) {
+      assertThat(result).isNotNull();
+    }
+
     return true;
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/OQLQuery.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/OQLQuery.java
index eb1e3e0..16fcdaa 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/OQLQuery.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/OQLQuery.java
@@ -14,6 +14,8 @@
  */
 package org.apache.geode.benchmark.tasks;
 
+import static org.apache.geode.util.internal.UncheckedUtils.uncheckedCast;
+
 import java.io.Serializable;
 import java.util.Map;
 import java.util.concurrent.ThreadLocalRandom;
@@ -25,7 +27,6 @@ import org.yardstickframework.BenchmarkConfiguration;
 import org.yardstickframework.BenchmarkDriverAdapter;
 
 import org.apache.geode.benchmark.LongRange;
-import org.apache.geode.cache.Region;
 import org.apache.geode.cache.client.ClientCache;
 import org.apache.geode.cache.client.ClientCacheFactory;
 import org.apache.geode.cache.query.Query;
@@ -34,42 +35,48 @@ import org.apache.geode.perftest.jvms.RemoteJVMFactory;
 
 public class OQLQuery extends BenchmarkDriverAdapter implements Serializable {
   private static final Logger logger = LoggerFactory.getLogger(RemoteJVMFactory.class);
-  private Region<Object, Object> region;
-  private LongRange keyRange;
-  private long queryRange;
-  private ClientCache cache;
+
+  private final LongRange keyRange;
+  private final long queryRange;
+  private final boolean isValidationEnabled;
+
   private Query query;
 
 
-  public OQLQuery(LongRange keyRange, long queryRange) {
+  public OQLQuery(final LongRange keyRange, final long queryRange,
+      final boolean isValidationEnabled) {
     this.keyRange = keyRange;
     this.queryRange = queryRange;
+    this.isValidationEnabled = isValidationEnabled;
   }
 
   @Override
   public void setUp(BenchmarkConfiguration cfg) throws Exception {
     super.setUp(cfg);
-    cache = ClientCacheFactory.getAnyInstance();
-    region = cache.getRegion("region");
+    final ClientCache cache = ClientCacheFactory.getAnyInstance();
     query =
         cache.getQueryService().newQuery("SELECT * FROM /region r WHERE r.ID >= $1 AND r.ID < $2");
   }
 
   @Override
-  public boolean test(Map<Object, Object> ctx) throws Exception {
-    long minId =
+  public boolean test(final Map<Object, Object> ctx) throws Exception {
+    final long minId =
         ThreadLocalRandom.current().nextLong(keyRange.getMin(), keyRange.getMax() - queryRange);
-    long maxId = minId + queryRange;
+    final long maxId = minId + queryRange;
 
-    SelectResults results = executeQuery(minId, maxId);
-    verifyResults(results, minId, maxId);
+    final Object result = query.execute(minId, maxId);
+
+    if (isValidationEnabled) {
+      verifyResults(uncheckedCast(result), minId, maxId);
+    }
 
     return true;
   }
 
-  private void verifyResults(SelectResults results, long minId, long maxId) throws Exception {
-    for (Object result : results) {
-      long id = ((Portfolio) result).getID();
+  private void verifyResults(final SelectResults<Portfolio> results, final long minId,
+      final long maxId) throws Exception {
+    for (final Portfolio result : results) {
+      final long id = result.getID();
       if (id < minId || id > maxId) {
         throw new Exception("Invalid Portfolio object retrieved [min =" + minId + " max =" + maxId
             + ") Portfolio retrieved =" + result);
@@ -77,7 +84,4 @@ public class OQLQuery extends BenchmarkDriverAdapter implements Serializable {
     }
   }
 
-  private SelectResults executeQuery(long minId, long maxId) throws Exception {
-    return (SelectResults) query.execute(minId, maxId);
-  }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegion.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegion.java
index 2b633ef..d7c8ad0 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegion.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegion.java
@@ -22,7 +22,7 @@ import benchmark.geode.data.Portfolio;
 import org.apache.geode.benchmark.LongRange;
 import org.apache.geode.benchmark.topology.Roles;
 
-public class PrePopulateRegion extends AbstractPrePopulateRegion<Portfolio> {
+public class PrePopulateRegion extends AbstractPrePopulateRegion<Long, Portfolio> {
 
   public PrePopulateRegion() {}
 
@@ -36,7 +36,12 @@ public class PrePopulateRegion extends AbstractPrePopulateRegion<Portfolio> {
   }
 
   @Override
-  protected Portfolio getValue(long i) {
+  protected Long getKey(final long i) {
+    return i;
+  }
+
+  @Override
+  protected Portfolio getValue(final long i) {
     return new Portfolio(i);
   }
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegionBytes.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegionBytes.java
index 73ae3a3..e132fd2 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegionBytes.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegionBytes.java
@@ -20,7 +20,7 @@ package org.apache.geode.benchmark.tasks;
 import org.apache.geode.benchmark.LongRange;
 import org.apache.geode.benchmark.topology.Roles;
 
-public class PrePopulateRegionBytes extends AbstractPrePopulateRegion<byte[]> {
+public class PrePopulateRegionBytes extends AbstractPrePopulateRegion<Long, byte[]> {
 
   public PrePopulateRegionBytes() {}
 
@@ -34,7 +34,12 @@ public class PrePopulateRegionBytes extends AbstractPrePopulateRegion<byte[]> {
   }
 
   @Override
-  protected byte[] getValue(long i) {
+  protected Long getKey(final long i) {
+    return i;
+  }
+
+  @Override
+  protected byte[] getValue(final long i) {
     return new byte[1204];
   }
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegionLong.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegionLong.java
index 49f4725..1872665 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegionLong.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegionLong.java
@@ -20,7 +20,7 @@ package org.apache.geode.benchmark.tasks;
 import org.apache.geode.benchmark.LongRange;
 import org.apache.geode.benchmark.topology.Roles;
 
-public class PrePopulateRegionLong extends AbstractPrePopulateRegion<Long> {
+public class PrePopulateRegionLong extends AbstractPrePopulateRegion<Long, Long> {
 
   public PrePopulateRegionLong() {}
 
@@ -34,7 +34,12 @@ public class PrePopulateRegionLong extends AbstractPrePopulateRegion<Long> {
   }
 
   @Override
-  protected Long getValue(long i) {
+  protected Long getKey(final long i) {
+    return i;
+  }
+
+  @Override
+  protected Long getValue(final long i) {
     return i;
   }
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegion.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegionString.java
similarity index 70%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegion.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegionString.java
index 2b633ef..d572a2e 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegion.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PrePopulateRegionString.java
@@ -17,27 +17,32 @@
 
 package org.apache.geode.benchmark.tasks;
 
-import benchmark.geode.data.Portfolio;
+import static java.lang.String.valueOf;
 
 import org.apache.geode.benchmark.LongRange;
 import org.apache.geode.benchmark.topology.Roles;
 
-public class PrePopulateRegion extends AbstractPrePopulateRegion<Portfolio> {
+public class PrePopulateRegionString extends AbstractPrePopulateRegion<String, String> {
 
-  public PrePopulateRegion() {}
+  public PrePopulateRegionString() {}
 
-  public PrePopulateRegion(LongRange keyRangeToPrepopulate) {
+  public PrePopulateRegionString(LongRange keyRangeToPrepopulate) {
     super(keyRangeToPrepopulate);
   }
 
-  public PrePopulateRegion(LongRange keyRangeToPrepopulate,
+  public PrePopulateRegionString(LongRange keyRangeToPrepopulate,
       Roles targetRole) {
     super(keyRangeToPrepopulate, targetRole);
   }
 
   @Override
-  protected Portfolio getValue(long i) {
-    return new Portfolio(i);
+  protected String getKey(final long i) {
+    return valueOf(i);
+  }
+
+  @Override
+  protected String getValue(final long i) {
+    return valueOf(i);
   }
 
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/ProcessControl.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/ProcessControl.java
index 562af01..e0311c3 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/ProcessControl.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/ProcessControl.java
@@ -14,24 +14,63 @@
  */
 package org.apache.geode.benchmark.tasks;
 
+import static java.lang.String.format;
+import static java.lang.String.join;
+
 import java.io.IOException;
+import java.time.Duration;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class ProcessControl {
+  private static final Duration RETRY_TIMEOUT = Duration.ofMinutes(1);
 
   private static final Logger logger = LoggerFactory.getLogger(ProcessControl.class);
 
-  void runCommand(final String command) throws IOException, InterruptedException {
+  public static void runCommand(final String command) throws IOException, InterruptedException {
     final Process startDaemon = Runtime.getRuntime().exec(command);
     final int exitStatus = startDaemon.waitFor();
     if (exitStatus != 0) {
-      final String msg = String.format("'%s' command exited with status %d\npwd is: %s", command,
+      final String msg = format("'%s' command exited with status %d\npwd is: %s", command,
           exitStatus, System.getProperty("user.dir"));
       logger.error(msg);
       throw new IllegalStateException(msg);
     }
   }
 
+  public static void runAndExpectZeroExit(final ProcessBuilder processBuilder)
+      throws IOException, InterruptedException {
+    final Process process = processBuilder.start();
+    final int exitStatus = process.waitFor();
+    if (exitStatus != 0) {
+      final String msg =
+          format("'%s' command exited with status %d", join(" ", processBuilder.command()),
+              exitStatus, System.getProperty("user.dir"));
+      logger.error(msg);
+      throw new IllegalStateException(msg);
+    }
+  }
+
+  public static void retryUntilZeroExit(final ProcessBuilder processBuilder)
+      throws IOException, InterruptedException {
+    long start = System.nanoTime();
+    while (true) {
+      final Process process = processBuilder.start();
+      final int exitStatus = process.waitFor();
+      if (exitStatus != 0) {
+        final String msg =
+            format("'%s' command exited with status %d", join(" ", processBuilder.command()),
+                exitStatus, System.getProperty("user.dir"));
+        logger.error(msg);
+        if (System.nanoTime() - start > RETRY_TIMEOUT.toNanos()) {
+          throw new RuntimeException(msg);
+        }
+        Thread.sleep(100);
+        continue;
+      }
+      break;
+    }
+  }
+
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/GetTask.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PutStringTask.java
similarity index 76%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/GetTask.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PutStringTask.java
index f6d701d..3e1becc 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/GetTask.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/PutStringTask.java
@@ -17,6 +17,8 @@
 
 package org.apache.geode.benchmark.tasks;
 
+import static java.lang.String.valueOf;
+
 import java.io.Serializable;
 import java.util.Map;
 
@@ -32,13 +34,15 @@ import org.apache.geode.cache.Region;
  * Task workload to perform get operations on keys within 0
  * and the keyRange (exclusive)
  */
-public class GetTask extends BenchmarkDriverAdapter implements Serializable {
+public class PutStringTask extends BenchmarkDriverAdapter implements Serializable {
 
   private final LongRange keyRange;
 
-  private Region<Object, Object> region;
+  private Region<String, String> region;
+  private long offset;
+  private String[] keys;
 
-  public GetTask(LongRange keyRange) {
+  public PutStringTask(LongRange keyRange) {
     this.keyRange = keyRange;
   }
 
@@ -48,12 +52,16 @@ public class GetTask extends BenchmarkDriverAdapter implements Serializable {
 
     final Cache cache = CacheFactory.getAnyInstance();
     region = cache.getRegion("region");
+
+    offset = keyRange.getMin();
+    keys = new String[(int) (keyRange.getMax() - offset)];
+    keyRange.forEach(i -> keys[(int) i] = valueOf(i));
   }
 
   @Override
   public boolean test(Map<Object, Object> ctx) throws Exception {
-    final long key = keyRange.random();
-    region.get(key);
+    final String key = keys[(int) (keyRange.random() - offset)];
+    region.put(key, key);
     return true;
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartLocator.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartLocator.java
index 923ec10..48637c6 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartLocator.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartLocator.java
@@ -47,6 +47,7 @@ public class StartLocator implements Task {
     properties.setProperty(ConfigurationProperties.STATISTIC_ARCHIVE_FILE, statsFile);
 
     properties.setProperty(ConfigurationProperties.NAME, "locator-" + InetAddress.getLocalHost());
+
     startLocator(properties, locatorPort, context);
   }
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartServer.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartServer.java
index 6118eda..56bac58 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartServer.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StartServer.java
@@ -23,10 +23,10 @@ import java.io.File;
 import java.net.InetAddress;
 import java.util.Properties;
 
-import org.apache.geode.cache.Cache;
 import org.apache.geode.cache.CacheFactory;
 import org.apache.geode.cache.server.CacheServer;
 import org.apache.geode.distributed.ConfigurationProperties;
+import org.apache.geode.internal.cache.InternalCache;
 import org.apache.geode.pdx.ReflectionBasedAutoSerializer;
 import org.apache.geode.perftest.Task;
 import org.apache.geode.perftest.TestContext;
@@ -36,6 +36,8 @@ import org.apache.geode.perftest.TestContext;
  */
 public class StartServer implements Task {
 
+  public static final String SERVER_CACHE = "SERVER_CACHE";
+
   private final int locatorPort;
   private final int serverPort;
 
@@ -49,20 +51,37 @@ public class StartServer implements Task {
 
     Properties properties = serverProperties();
 
+    final CacheFactory cacheFactory = new CacheFactory(properties);
+    configureCacheFactory(cacheFactory, context);
+    final InternalCache cache = (InternalCache) cacheFactory.create();
+
+    final CacheServer cacheServer = configureCacheServer(cache.addCacheServer(), context);
+    if (null != cacheServer) {
+      cacheServer.start();
+    }
+
+    context.setAttribute(SERVER_CACHE, cache);
+  }
+
+  /**
+   * Configure the {@link CacheFactory}
+   *
+   * Subclasses can override this. Call super first to inherit settings.
+   *
+   * @param cacheFactory is modified by this method!
+   */
+  protected CacheFactory configureCacheFactory(final CacheFactory cacheFactory,
+      final TestContext context)
+      throws Exception {
     String locatorString = LocatorUtil.getLocatorString(context, locatorPort);
     String statsFile = new File(context.getOutputDir(), "stats.gfs").getAbsolutePath();
-    Cache cache = new CacheFactory(properties)
+
+    return cacheFactory
         .setPdxSerializer(new ReflectionBasedAutoSerializer("benchmark.geode.data.*"))
         .set(ConfigurationProperties.LOCATORS, locatorString)
         .set(ConfigurationProperties.NAME,
             "server-" + context.getJvmID() + "-" + InetAddress.getLocalHost())
-        .set(ConfigurationProperties.STATISTIC_ARCHIVE_FILE, statsFile)
-        .create();
-    CacheServer cacheServer = cache.addCacheServer();
-    configureCacheServer(cacheServer, context);
-    cacheServer.start();
-    context.setAttribute("SERVER_CACHE", cache);
-
+        .set(ConfigurationProperties.STATISTIC_ARCHIVE_FILE, statsFile);
   }
 
   /**
@@ -72,9 +91,11 @@ public class StartServer implements Task {
    *
    * @param cacheServer is modified by this method!
    */
-  protected void configureCacheServer(final CacheServer cacheServer, final TestContext context) {
+  protected CacheServer configureCacheServer(final CacheServer cacheServer,
+      final TestContext context) {
     cacheServer.setMaxConnections(Integer.MAX_VALUE);
     cacheServer.setPort(serverPort);
+    return cacheServer;
   }
 
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java
index 6ffd348..0752cfb 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/StopServer.java
@@ -17,6 +17,8 @@
 
 package org.apache.geode.benchmark.tasks;
 
+import static org.apache.geode.benchmark.tasks.StartServer.SERVER_CACHE;
+
 import org.apache.geode.cache.Cache;
 import org.apache.geode.perftest.Task;
 import org.apache.geode.perftest.TestContext;
@@ -28,7 +30,7 @@ public class StopServer implements Task {
 
   @Override
   public void run(final TestContext context) throws Exception {
-    final Cache cache = (Cache) context.getAttribute("SERVER_CACHE");
+    final Cache cache = (Cache) context.getAttribute(SERVER_CACHE);
     cache.close();
   }
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/WeightedTasks.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/WeightedTasks.java
new file mode 100644
index 0000000..bf8a523
--- /dev/null
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tasks/WeightedTasks.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.benchmark.tasks;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.concurrent.ThreadLocalRandom;
+
+import org.yardstickframework.BenchmarkConfiguration;
+import org.yardstickframework.BenchmarkDriver;
+
+public class WeightedTasks implements BenchmarkDriver, Serializable {
+
+  private final int totalWeight;
+
+  public static class WeightedTask implements Serializable {
+    int weight;
+    BenchmarkDriver task;
+
+    public WeightedTask(final int weight, final BenchmarkDriver task) {
+      this.weight = weight;
+      this.task = task;
+    }
+  }
+
+  private final WeightedTask[] weightedTasks;
+
+  public WeightedTasks(WeightedTask... weightedTasks) {
+    this.weightedTasks = weightedTasks;
+    this.totalWeight = Arrays.stream(weightedTasks).mapToInt(wt -> wt.weight).sum();
+  }
+
+
+  @Override
+  public void setUp(final BenchmarkConfiguration benchmarkConfiguration) throws Exception {
+    for (final WeightedTask weightedTask : weightedTasks) {
+      weightedTask.task.setUp(benchmarkConfiguration);
+    }
+  }
+
+  @Override
+  public boolean test(final Map<Object, Object> context) throws Exception {
+    int weight = ThreadLocalRandom.current().nextInt(totalWeight) + 1;
+    for (final WeightedTask weightedTask : weightedTasks) {
+      weight -= weightedTask.weight;
+      if (weight > 0) {
+        continue;
+      }
+
+      return weightedTask.task.test(context);
+    }
+
+    return true;
+  }
+
+  @Override
+  public void tearDown() throws Exception {
+    for (final WeightedTask weightedTask : weightedTasks) {
+      weightedTask.task.tearDown();
+    }
+  }
+
+  @Override
+  public String description() {
+    final StringBuilder stringBuilder = new StringBuilder("Composite Task:\n");
+    for (final WeightedTask weightedTask : weightedTasks) {
+      stringBuilder.append(weightedTask.weight).append(" ").append(weightedTask.task.description())
+          .append("\n");
+    }
+    return stringBuilder.toString();
+  }
+
+  @Override
+  public String usage() {
+    final StringBuilder stringBuilder = new StringBuilder("Composite Task: \n");
+    for (final WeightedTask weightedTask : weightedTasks) {
+      stringBuilder.append(weightedTask.weight).append(" ").append(weightedTask.task.usage())
+          .append("\n");
+    }
+    return stringBuilder.toString();
+  }
+
+  @Override
+  public void onWarmupFinished() {
+    for (final WeightedTask weightedTask : weightedTasks) {
+      weightedTask.task.onWarmupFinished();
+    }
+  }
+
+  @Override
+  public void onException(final Throwable e) {
+    for (final WeightedTask weightedTask : weightedTasks) {
+      weightedTask.task.onException(e);
+    }
+  }
+}
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/AbstractFunctionBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/AbstractFunctionBenchmark.java
index 635b377..ba20505 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/AbstractFunctionBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/AbstractFunctionBenchmark.java
@@ -25,10 +25,9 @@ import org.apache.geode.benchmark.LongRange;
 import org.apache.geode.benchmark.tasks.CreateClientProxyRegion;
 import org.apache.geode.benchmark.tasks.PrePopulateRegion;
 import org.apache.geode.benchmark.tasks.RegisterFunction;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 
-abstract class AbstractFunctionBenchmark implements PerformanceTest {
+abstract class AbstractFunctionBenchmark extends AbstractPerformanceTest {
   private LongRange keyRange = new LongRange(0, 1000000);
 
   public final void setKeyRange(LongRange keyRange) {
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/AbstractPerformanceTest.java
similarity index 64%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/AbstractPerformanceTest.java
index 922ff51..9593a6a 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/AbstractPerformanceTest.java
@@ -13,8 +13,18 @@
  * the License.
  */
 
-package org.apache.geode.benchmark.parameters;
+package org.apache.geode.benchmark.tests;
 
-public enum GcImplementation {
-  CMS, G1, Z, Shenandoah;
+import org.apache.geode.perftest.PerformanceTest;
+
+public abstract class AbstractPerformanceTest implements PerformanceTest {
+  private boolean validationEnabled = GeodeBenchmark.isValidationEnabled();
+
+  public void setValidationEnabled(final boolean validationEnabled) {
+    this.validationEnabled = validationEnabled;
+  }
+
+  public boolean isValidationEnabled() {
+    return validationEnabled;
+  }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/GeodeBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/GeodeBenchmark.java
index 97e3494..358ed34 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/GeodeBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/GeodeBenchmark.java
@@ -36,6 +36,11 @@ public class GeodeBenchmark {
    */
   private static final int THREADS = Runtime.getRuntime().availableProcessors() * 10;
 
+  public static final String WITH_MIN_KEY = "benchmark.withMinKey";
+  public static final String WITH_MAX_KEY = "benchmark.withMaxKey";
+  public static final String WITH_VALIDATION_PROPERTY = "benchmark.withValidation";
+  public static final String WITH_REPLICAS = "benchmark.withReplicas";
+  public static final String WITH_BUCKETS = "benchmark.withBuckets";
 
   public static TestConfig createConfig() {
     TestConfig config = new TestConfig();
@@ -45,4 +50,7 @@ public class GeodeBenchmark {
     return config;
   }
 
+  public static boolean isValidationEnabled() {
+    return Boolean.getBoolean(WITH_VALIDATION_PROPERTY);
+  }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java
index dbc9a12..444d790 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/NoopBenchmark.java
@@ -24,14 +24,13 @@ import static org.apache.geode.benchmark.topology.Roles.CLIENT;
 import org.junit.jupiter.api.Test;
 
 import org.apache.geode.benchmark.tasks.NoopTask;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of gets on a partitioned region.
  */
-public class NoopBenchmark implements PerformanceTest {
+public class NoopBenchmark extends AbstractPerformanceTest {
 
   @Test
   public void run() throws Exception {
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/P2pPartitionedGetBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/P2pPartitionedGetBenchmark.java
index 9c7709f..9a4e48f 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/P2pPartitionedGetBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/P2pPartitionedGetBenchmark.java
@@ -28,14 +28,13 @@ import org.apache.geode.benchmark.LongRange;
 import org.apache.geode.benchmark.tasks.CreatePartitionedRegion;
 import org.apache.geode.benchmark.tasks.GetTask;
 import org.apache.geode.benchmark.tasks.PrePopulateRegion;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of gets on a partitioned region.
  */
-public class P2pPartitionedGetBenchmark implements PerformanceTest {
+public class P2pPartitionedGetBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1000000);
 
@@ -55,7 +54,7 @@ public class P2pPartitionedGetBenchmark implements PerformanceTest {
     final TestConfig config = P2pBenchmark.createConfig();
     before(config, new CreatePartitionedRegion(), SERVER);
     before(config, new PrePopulateRegion(keyRange, SERVER), SERVER);
-    workload(config, new GetTask(keyRange), SERVER);
+    workload(config, new GetTask(keyRange, isValidationEnabled()), SERVER);
     return config;
 
   }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/P2pPartitionedPutBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/P2pPartitionedPutBenchmark.java
index 4ae1b3c..92ac8fc 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/P2pPartitionedPutBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/P2pPartitionedPutBenchmark.java
@@ -27,14 +27,13 @@ import org.apache.geode.benchmark.LongRange;
 import org.apache.geode.benchmark.tasks.CreatePartitionedRegion;
 import org.apache.geode.benchmark.tasks.PrePopulateRegion;
 import org.apache.geode.benchmark.tasks.PutTask;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of puts on a partitioned region.
  */
-public class P2pPartitionedPutBenchmark implements PerformanceTest {
+public class P2pPartitionedPutBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1_000_000);
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/P2pPartitionedPutBytesBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/P2pPartitionedPutBytesBenchmark.java
index 2e4af47..44a377f 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/P2pPartitionedPutBytesBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/P2pPartitionedPutBytesBenchmark.java
@@ -27,14 +27,13 @@ import org.apache.geode.benchmark.LongRange;
 import org.apache.geode.benchmark.tasks.CreatePartitionedRegion;
 import org.apache.geode.benchmark.tasks.PrePopulateRegionBytes;
 import org.apache.geode.benchmark.tasks.PutBytesTask;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of puts on a partitioned region.
  */
-public class P2pPartitionedPutBytesBenchmark implements PerformanceTest {
+public class P2pPartitionedPutBytesBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1000000);
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/P2pPartitionedPutLongBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/P2pPartitionedPutLongBenchmark.java
index 961b604..0b37803 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/P2pPartitionedPutLongBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/P2pPartitionedPutLongBenchmark.java
@@ -27,14 +27,13 @@ import org.apache.geode.benchmark.LongRange;
 import org.apache.geode.benchmark.tasks.CreatePartitionedRegion;
 import org.apache.geode.benchmark.tasks.PrePopulateRegionLong;
 import org.apache.geode.benchmark.tasks.PutLongTask;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of puts on a partitioned region.
  */
-public class P2pPartitionedPutLongBenchmark implements PerformanceTest {
+public class P2pPartitionedPutLongBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1000000);
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionBenchmark.java
index 381d4d6..ac62892 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionBenchmark.java
@@ -33,7 +33,7 @@ public class PartitionedFunctionExecutionBenchmark extends AbstractPartitionedFu
   @Override
   public TestConfig configure() {
     TestConfig config = super.configure();
-    workload(config, new ExecuteFunction(), CLIENT);
+    workload(config, new ExecuteFunction(isValidationEnabled()), CLIENT);
     return config;
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionWithArgumentsBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionWithArgumentsBenchmark.java
index 9de3051..c0a8aeb 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionWithArgumentsBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionWithArgumentsBenchmark.java
@@ -36,7 +36,8 @@ public class PartitionedFunctionExecutionWithArgumentsBenchmark
   public TestConfig configure() {
     TestConfig config = super.configure();
     config.threads(Runtime.getRuntime().availableProcessors() * 4);
-    workload(config, new ExecuteParameterizedFunction(getKeyRange()), CLIENT);
+    workload(config, new ExecuteParameterizedFunction(getKeyRange(), isValidationEnabled()),
+        CLIENT);
     return config;
 
   }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionWithFiltersBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionWithFiltersBenchmark.java
index 3a3d735..2dfad68 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionWithFiltersBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionWithFiltersBenchmark.java
@@ -35,7 +35,7 @@ public class PartitionedFunctionExecutionWithFiltersBenchmark
   public TestConfig configure() {
     TestConfig config = super.configure();
     config.threads(Runtime.getRuntime().availableProcessors() * 8);
-    workload(config, new ExecuteFilteredFunction(getKeyRange()), CLIENT);
+    workload(config, new ExecuteFilteredFunction(getKeyRange(), isValidationEnabled()), CLIENT);
     return config;
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedGetBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedGetBenchmark.java
index d2a6790..1c93707 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedGetBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedGetBenchmark.java
@@ -30,14 +30,13 @@ import org.apache.geode.benchmark.tasks.CreateClientProxyRegion;
 import org.apache.geode.benchmark.tasks.CreatePartitionedRegion;
 import org.apache.geode.benchmark.tasks.GetTask;
 import org.apache.geode.benchmark.tasks.PrePopulateRegion;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of gets on a partitioned region.
  */
-public class PartitionedGetBenchmark implements PerformanceTest {
+public class PartitionedGetBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1000000);
 
@@ -58,7 +57,7 @@ public class PartitionedGetBenchmark implements PerformanceTest {
     before(config, new CreatePartitionedRegion(), SERVER);
     before(config, new CreateClientProxyRegion(), CLIENT);
     before(config, new PrePopulateRegion(keyRange), CLIENT);
-    workload(config, new GetTask(keyRange), CLIENT);
+    workload(config, new GetTask(keyRange, isValidationEnabled()), CLIENT);
     return config;
 
   }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedGetLongBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedGetLongBenchmark.java
index 3b770c5..e359bb1 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedGetLongBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedGetLongBenchmark.java
@@ -30,14 +30,13 @@ import org.apache.geode.benchmark.tasks.CreateClientProxyRegion;
 import org.apache.geode.benchmark.tasks.CreatePartitionedRegion;
 import org.apache.geode.benchmark.tasks.GetTask;
 import org.apache.geode.benchmark.tasks.PrePopulateRegionLong;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of gets on a partitioned region.
  */
-public class PartitionedGetLongBenchmark implements PerformanceTest {
+public class PartitionedGetLongBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1000000);
 
@@ -58,7 +57,7 @@ public class PartitionedGetLongBenchmark implements PerformanceTest {
     before(config, new CreatePartitionedRegion(), SERVER);
     before(config, new CreateClientProxyRegion(), CLIENT);
     before(config, new PrePopulateRegionLong(keyRange), CLIENT);
-    workload(config, new GetTask(keyRange), CLIENT);
+    workload(config, new GetTask(keyRange, isValidationEnabled()), CLIENT);
     return config;
 
   }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedGetLongBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedGetStringBenchmark.java
similarity index 80%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedGetLongBenchmark.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedGetStringBenchmark.java
index 3b770c5..71fd491 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedGetLongBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedGetStringBenchmark.java
@@ -20,6 +20,7 @@ package org.apache.geode.benchmark.tests;
 
 import static org.apache.geode.benchmark.Config.before;
 import static org.apache.geode.benchmark.Config.workload;
+import static org.apache.geode.benchmark.tests.GeodeBenchmark.isValidationEnabled;
 import static org.apache.geode.benchmark.topology.Roles.CLIENT;
 import static org.apache.geode.benchmark.topology.Roles.SERVER;
 
@@ -28,16 +29,15 @@ import org.junit.jupiter.api.Test;
 import org.apache.geode.benchmark.LongRange;
 import org.apache.geode.benchmark.tasks.CreateClientProxyRegion;
 import org.apache.geode.benchmark.tasks.CreatePartitionedRegion;
-import org.apache.geode.benchmark.tasks.GetTask;
-import org.apache.geode.benchmark.tasks.PrePopulateRegionLong;
-import org.apache.geode.perftest.PerformanceTest;
+import org.apache.geode.benchmark.tasks.GetStringTask;
+import org.apache.geode.benchmark.tasks.PrePopulateRegionString;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of gets on a partitioned region.
  */
-public class PartitionedGetLongBenchmark implements PerformanceTest {
+public class PartitionedGetStringBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1000000);
 
@@ -46,7 +46,7 @@ public class PartitionedGetLongBenchmark implements PerformanceTest {
     TestRunners.defaultRunner().runTest(this);
   }
 
-  public PartitionedGetLongBenchmark() {}
+  public PartitionedGetStringBenchmark() {}
 
   public void setKeyRange(final LongRange keyRange) {
     this.keyRange = keyRange;
@@ -57,8 +57,8 @@ public class PartitionedGetLongBenchmark implements PerformanceTest {
     TestConfig config = ClientServerBenchmark.createConfig();
     before(config, new CreatePartitionedRegion(), SERVER);
     before(config, new CreateClientProxyRegion(), CLIENT);
-    before(config, new PrePopulateRegionLong(keyRange), CLIENT);
-    workload(config, new GetTask(keyRange), CLIENT);
+    before(config, new PrePopulateRegionString(keyRange), CLIENT);
+    workload(config, new GetStringTask(keyRange, isValidationEnabled()), CLIENT);
     return config;
 
   }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedIndexedQueryBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedIndexedQueryBenchmark.java
index bc81006..040de68 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedIndexedQueryBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedIndexedQueryBenchmark.java
@@ -27,11 +27,10 @@ import org.apache.geode.benchmark.tasks.CreateIndexOnID;
 import org.apache.geode.benchmark.tasks.CreatePartitionedRegion;
 import org.apache.geode.benchmark.tasks.OQLQuery;
 import org.apache.geode.benchmark.tasks.PrePopulateRegion;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
-public class PartitionedIndexedQueryBenchmark implements PerformanceTest {
+public class PartitionedIndexedQueryBenchmark extends AbstractPerformanceTest {
   private LongRange keyRange = new LongRange(0, 500000);
   private long queryRange = 100;
 
@@ -58,7 +57,7 @@ public class PartitionedIndexedQueryBenchmark implements PerformanceTest {
     before(config, new CreateClientProxyRegion(), CLIENT);
     before(config, new CreateIndexOnID(), SERVER);
     before(config, new PrePopulateRegion(keyRange), CLIENT);
-    workload(config, new OQLQuery(keyRange, queryRange), CLIENT);
+    workload(config, new OQLQuery(keyRange, queryRange, isValidationEnabled()), CLIENT);
     return config;
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedNonIndexedQueryBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedNonIndexedQueryBenchmark.java
index 80ff61c..8be31f6 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedNonIndexedQueryBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedNonIndexedQueryBenchmark.java
@@ -26,11 +26,10 @@ import org.apache.geode.benchmark.tasks.CreateClientProxyRegion;
 import org.apache.geode.benchmark.tasks.CreatePartitionedRegion;
 import org.apache.geode.benchmark.tasks.OQLQuery;
 import org.apache.geode.benchmark.tasks.PrePopulateRegion;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
-public class PartitionedNonIndexedQueryBenchmark implements PerformanceTest {
+public class PartitionedNonIndexedQueryBenchmark extends AbstractPerformanceTest {
   private LongRange keyRange = new LongRange(0, 500000);
   private long queryRange = 100;
 
@@ -56,7 +55,7 @@ public class PartitionedNonIndexedQueryBenchmark implements PerformanceTest {
     before(config, new CreatePartitionedRegion(), SERVER);
     before(config, new CreateClientProxyRegion(), CLIENT);
     before(config, new PrePopulateRegion(keyRange), CLIENT);
-    workload(config, new OQLQuery(keyRange, queryRange), CLIENT);
+    workload(config, new OQLQuery(keyRange, queryRange, isValidationEnabled()), CLIENT);
     return config;
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutAllBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutAllBenchmark.java
index f7f13f6..3b74757 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutAllBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutAllBenchmark.java
@@ -29,14 +29,13 @@ import org.apache.geode.benchmark.tasks.CreateClientProxyRegion;
 import org.apache.geode.benchmark.tasks.CreatePartitionedRegion;
 import org.apache.geode.benchmark.tasks.PrePopulateRegion;
 import org.apache.geode.benchmark.tasks.PutAllTask;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of putAlls on a partitioned region.
  */
-public class PartitionedPutAllBenchmark implements PerformanceTest {
+public class PartitionedPutAllBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1000000);
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutAllLongBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutAllLongBenchmark.java
index a1ad5e2..8fe9b00 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutAllLongBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutAllLongBenchmark.java
@@ -29,14 +29,13 @@ import org.apache.geode.benchmark.tasks.CreateClientProxyRegion;
 import org.apache.geode.benchmark.tasks.CreatePartitionedRegion;
 import org.apache.geode.benchmark.tasks.PrePopulateRegionLong;
 import org.apache.geode.benchmark.tasks.PutAllTask;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of putAlls on a partitioned region.
  */
-public class PartitionedPutAllLongBenchmark implements PerformanceTest {
+public class PartitionedPutAllLongBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1000000);
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutBenchmark.java
index 8a61a79..e2723fb 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutBenchmark.java
@@ -29,14 +29,13 @@ import org.apache.geode.benchmark.tasks.CreateClientProxyRegion;
 import org.apache.geode.benchmark.tasks.CreatePartitionedRegion;
 import org.apache.geode.benchmark.tasks.PrePopulateRegion;
 import org.apache.geode.benchmark.tasks.PutTask;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of puts on a partitioned region.
  */
-public class PartitionedPutBenchmark implements PerformanceTest {
+public class PartitionedPutBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1_000_000);
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutBytesBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutBytesBenchmark.java
index 2a70824..23ced06 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutBytesBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutBytesBenchmark.java
@@ -29,14 +29,13 @@ import org.apache.geode.benchmark.tasks.CreateClientProxyRegion;
 import org.apache.geode.benchmark.tasks.CreatePartitionedRegion;
 import org.apache.geode.benchmark.tasks.PrePopulateRegionBytes;
 import org.apache.geode.benchmark.tasks.PutBytesTask;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of puts on a partitioned region.
  */
-public class PartitionedPutBytesBenchmark implements PerformanceTest {
+public class PartitionedPutBytesBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1000000);
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutLongBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutLongBenchmark.java
index 5b5307f..907f50b 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutLongBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutLongBenchmark.java
@@ -29,14 +29,13 @@ import org.apache.geode.benchmark.tasks.CreateClientProxyRegion;
 import org.apache.geode.benchmark.tasks.CreatePartitionedRegion;
 import org.apache.geode.benchmark.tasks.PrePopulateRegionLong;
 import org.apache.geode.benchmark.tasks.PutLongTask;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of puts on a partitioned region.
  */
-public class PartitionedPutLongBenchmark implements PerformanceTest {
+public class PartitionedPutLongBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1000000);
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutStringBenchmark.java
similarity index 83%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutBenchmark.java
copy to geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutStringBenchmark.java
index 8a61a79..b8ebf25 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/PartitionedPutStringBenchmark.java
@@ -27,20 +27,19 @@ import org.junit.jupiter.api.Test;
 import org.apache.geode.benchmark.LongRange;
 import org.apache.geode.benchmark.tasks.CreateClientProxyRegion;
 import org.apache.geode.benchmark.tasks.CreatePartitionedRegion;
-import org.apache.geode.benchmark.tasks.PrePopulateRegion;
-import org.apache.geode.benchmark.tasks.PutTask;
-import org.apache.geode.perftest.PerformanceTest;
+import org.apache.geode.benchmark.tasks.PrePopulateRegionString;
+import org.apache.geode.benchmark.tasks.PutStringTask;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of puts on a partitioned region.
  */
-public class PartitionedPutBenchmark implements PerformanceTest {
+public class PartitionedPutStringBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1_000_000);
 
-  public PartitionedPutBenchmark() {}
+  public PartitionedPutStringBenchmark() {}
 
   public void setKeyRange(final LongRange keyRange) {
     this.keyRange = keyRange;
@@ -57,8 +56,8 @@ public class PartitionedPutBenchmark implements PerformanceTest {
 
     before(config, new CreatePartitionedRegion(), SERVER);
     before(config, new CreateClientProxyRegion(), CLIENT);
-    before(config, new PrePopulateRegion(keyRange), CLIENT);
-    workload(config, new PutTask(keyRange), CLIENT);
+    before(config, new PrePopulateRegionString(keyRange), CLIENT);
+    workload(config, new PutStringTask(keyRange), CLIENT);
     return config;
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionBenchmark.java
index 0b4e3c1..acf9212 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionBenchmark.java
@@ -33,7 +33,7 @@ public class ReplicatedFunctionExecutionBenchmark extends AbstractReplicatedFunc
   @Override
   public TestConfig configure() {
     TestConfig config = super.configure();
-    workload(config, new ExecuteFunction(), CLIENT);
+    workload(config, new ExecuteFunction(isValidationEnabled()), CLIENT);
     return config;
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionWithArgumentsBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionWithArgumentsBenchmark.java
index 595e955..1ded5a6 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionWithArgumentsBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionWithArgumentsBenchmark.java
@@ -35,7 +35,8 @@ public class ReplicatedFunctionExecutionWithArgumentsBenchmark
   public TestConfig configure() {
     TestConfig config = super.configure();
     config.threads(Runtime.getRuntime().availableProcessors() * 16);
-    workload(config, new ExecuteParameterizedFunction(getKeyRange()), CLIENT);
+    workload(config, new ExecuteParameterizedFunction(getKeyRange(), isValidationEnabled()),
+        CLIENT);
     return config;
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionWithFiltersBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionWithFiltersBenchmark.java
index 728fbab..39c4dc1 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionWithFiltersBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionWithFiltersBenchmark.java
@@ -35,7 +35,7 @@ public class ReplicatedFunctionExecutionWithFiltersBenchmark
   public TestConfig configure() {
     TestConfig config = super.configure();
     config.threads(Runtime.getRuntime().availableProcessors() * 10);
-    workload(config, new ExecuteFilteredFunction(getKeyRange()), CLIENT);
+    workload(config, new ExecuteFilteredFunction(getKeyRange(), isValidationEnabled()), CLIENT);
     return config;
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedGetBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedGetBenchmark.java
index ac4e9a3..87e2da7 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedGetBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedGetBenchmark.java
@@ -30,14 +30,13 @@ import org.apache.geode.benchmark.tasks.CreateClientProxyRegion;
 import org.apache.geode.benchmark.tasks.CreateReplicatedRegion;
 import org.apache.geode.benchmark.tasks.GetTask;
 import org.apache.geode.benchmark.tasks.PrePopulateRegion;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of gets on a replicated region.
  */
-public class ReplicatedGetBenchmark implements PerformanceTest {
+public class ReplicatedGetBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1000000);
 
@@ -58,7 +57,7 @@ public class ReplicatedGetBenchmark implements PerformanceTest {
     before(config, new CreateReplicatedRegion(), SERVER);
     before(config, new CreateClientProxyRegion(), CLIENT);
     before(config, new PrePopulateRegion(keyRange), CLIENT);
-    workload(config, new GetTask(keyRange), CLIENT);
+    workload(config, new GetTask(keyRange, isValidationEnabled()), CLIENT);
     return config;
 
   }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedGetLongBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedGetLongBenchmark.java
index c3215b9..8330c2c 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedGetLongBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedGetLongBenchmark.java
@@ -30,14 +30,13 @@ import org.apache.geode.benchmark.tasks.CreateClientProxyRegion;
 import org.apache.geode.benchmark.tasks.CreateReplicatedRegion;
 import org.apache.geode.benchmark.tasks.GetTask;
 import org.apache.geode.benchmark.tasks.PrePopulateRegionLong;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of gets on a replicated region.
  */
-public class ReplicatedGetLongBenchmark implements PerformanceTest {
+public class ReplicatedGetLongBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1000000);
 
@@ -58,7 +57,7 @@ public class ReplicatedGetLongBenchmark implements PerformanceTest {
     before(config, new CreateReplicatedRegion(), SERVER);
     before(config, new CreateClientProxyRegion(), CLIENT);
     before(config, new PrePopulateRegionLong(keyRange), CLIENT);
-    workload(config, new GetTask(keyRange), CLIENT);
+    workload(config, new GetTask(keyRange, isValidationEnabled()), CLIENT);
     return config;
 
   }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedIndexedQueryBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedIndexedQueryBenchmark.java
index 258400e..04ef8f6 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedIndexedQueryBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedIndexedQueryBenchmark.java
@@ -27,11 +27,10 @@ import org.apache.geode.benchmark.tasks.CreateIndexOnID;
 import org.apache.geode.benchmark.tasks.CreateReplicatedRegion;
 import org.apache.geode.benchmark.tasks.OQLQuery;
 import org.apache.geode.benchmark.tasks.PrePopulateRegion;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
-public class ReplicatedIndexedQueryBenchmark implements PerformanceTest {
+public class ReplicatedIndexedQueryBenchmark extends AbstractPerformanceTest {
   private LongRange keyRange = new LongRange(0, 500000);
   private long queryRange = 100;
 
@@ -58,7 +57,7 @@ public class ReplicatedIndexedQueryBenchmark implements PerformanceTest {
     before(config, new CreateClientProxyRegion(), CLIENT);
     before(config, new CreateIndexOnID(), SERVER);
     before(config, new PrePopulateRegion(keyRange), CLIENT);
-    workload(config, new OQLQuery(keyRange, queryRange), CLIENT);
+    workload(config, new OQLQuery(keyRange, queryRange, isValidationEnabled()), CLIENT);
     return config;
   }
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedNonIndexedQueryBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedNonIndexedQueryBenchmark.java
index 0858a9c..0c4d6b6 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedNonIndexedQueryBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedNonIndexedQueryBenchmark.java
@@ -26,11 +26,10 @@ import org.apache.geode.benchmark.tasks.CreateClientProxyRegion;
 import org.apache.geode.benchmark.tasks.CreateReplicatedRegion;
 import org.apache.geode.benchmark.tasks.OQLQuery;
 import org.apache.geode.benchmark.tasks.PrePopulateRegion;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
-public class ReplicatedNonIndexedQueryBenchmark implements PerformanceTest {
+public class ReplicatedNonIndexedQueryBenchmark extends AbstractPerformanceTest {
   private LongRange keyRange = new LongRange(0, 500000);
   private long queryRange = 100;
 
@@ -56,7 +55,7 @@ public class ReplicatedNonIndexedQueryBenchmark implements PerformanceTest {
     before(config, new CreateReplicatedRegion(), SERVER);
     before(config, new CreateClientProxyRegion(), CLIENT);
     before(config, new PrePopulateRegion(keyRange), CLIENT);
-    workload(config, new OQLQuery(keyRange, queryRange), CLIENT);
+    workload(config, new OQLQuery(keyRange, queryRange, isValidationEnabled()), CLIENT);
     return config;
   }
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedPutAllBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedPutAllBenchmark.java
index 72e7d1a..c5ace06 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedPutAllBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedPutAllBenchmark.java
@@ -29,14 +29,13 @@ import org.apache.geode.benchmark.tasks.CreateClientProxyRegion;
 import org.apache.geode.benchmark.tasks.CreateReplicatedRegion;
 import org.apache.geode.benchmark.tasks.PrePopulateRegion;
 import org.apache.geode.benchmark.tasks.PutAllTask;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of putAlls on a replicated region.
  */
-public class ReplicatedPutAllBenchmark implements PerformanceTest {
+public class ReplicatedPutAllBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1000000);
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedPutAllLongBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedPutAllLongBenchmark.java
index 1c7607b..3d45dac 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedPutAllLongBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedPutAllLongBenchmark.java
@@ -29,14 +29,13 @@ import org.apache.geode.benchmark.tasks.CreateClientProxyRegion;
 import org.apache.geode.benchmark.tasks.CreateReplicatedRegion;
 import org.apache.geode.benchmark.tasks.PrePopulateRegionLong;
 import org.apache.geode.benchmark.tasks.PutAllTask;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of putAlls on a replicated region.
  */
-public class ReplicatedPutAllLongBenchmark implements PerformanceTest {
+public class ReplicatedPutAllLongBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1000000);
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedPutBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedPutBenchmark.java
index 4339ca8..fd3c3f5 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedPutBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedPutBenchmark.java
@@ -29,14 +29,13 @@ import org.apache.geode.benchmark.tasks.CreateClientProxyRegion;
 import org.apache.geode.benchmark.tasks.CreateReplicatedRegion;
 import org.apache.geode.benchmark.tasks.PrePopulateRegion;
 import org.apache.geode.benchmark.tasks.PutTask;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of puts on a replicated region.
  */
-public class ReplicatedPutBenchmark implements PerformanceTest {
+public class ReplicatedPutBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1000000);
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedPutLongBenchmark.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedPutLongBenchmark.java
index 9b4d0f7..bc8c8e1 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedPutLongBenchmark.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/tests/ReplicatedPutLongBenchmark.java
@@ -29,14 +29,13 @@ import org.apache.geode.benchmark.tasks.CreateClientProxyRegion;
 import org.apache.geode.benchmark.tasks.CreateReplicatedRegion;
 import org.apache.geode.benchmark.tasks.PrePopulateRegionLong;
 import org.apache.geode.benchmark.tasks.PutTask;
-import org.apache.geode.perftest.PerformanceTest;
 import org.apache.geode.perftest.TestConfig;
 import org.apache.geode.perftest.TestRunners;
 
 /**
  * Benchmark of puts on a replicated region.
  */
-public class ReplicatedPutLongBenchmark implements PerformanceTest {
+public class ReplicatedPutLongBenchmark extends AbstractPerformanceTest {
 
   private LongRange keyRange = new LongRange(0, 1000000);
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopology.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopology.java
index 3678596..5de8c09 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopology.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopology.java
@@ -32,9 +32,9 @@ import org.apache.geode.benchmark.tasks.StopServer;
 import org.apache.geode.perftest.TestConfig;
 
 public class ClientServerTopology extends Topology {
-  private static final int NUM_LOCATORS = 1;
-  private static final int NUM_SERVERS = 2;
-  private static final int NUM_CLIENTS = 1;
+  private static final int NUM_LOCATORS = Integer.getInteger(WITH_LOCATOR_COUNT_PROPERTY, 1);
+  private static final int NUM_SERVERS = Integer.getInteger(WITH_SERVER_COUNT_PROPERTY, 2);
+  private static final int NUM_CLIENTS = Integer.getInteger(WITH_CLIENT_COUNT_PROPERTY, 1);
 
   public static void configure(TestConfig config) {
     role(config, LOCATOR, NUM_LOCATORS);
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithRouterAndSniProxy.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithRouterAndSniProxy.java
index b5b3467..b7bc850 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithRouterAndSniProxy.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithRouterAndSniProxy.java
@@ -38,9 +38,9 @@ public class ClientServerTopologyWithRouterAndSniProxy extends ClientServerTopol
   public static final String WITH_ROUTER_PROPERTY = "benchmark.withRouter";
   public static final String WITH_ROUTER_IMAGE_PROPERTY = "benchmark.withRouterImage";
 
-  private static final int NUM_LOCATORS = 1;
-  private static final int NUM_SERVERS = 2;
-  private static final int NUM_CLIENTS = 1;
+  private static final int NUM_LOCATORS = Integer.getInteger(WITH_LOCATOR_COUNT_PROPERTY, 1);
+  private static final int NUM_SERVERS = Integer.getInteger(WITH_SERVER_COUNT_PROPERTY, 2);
+  private static final int NUM_CLIENTS = Integer.getInteger(WITH_CLIENT_COUNT_PROPERTY, 1);
   private static final int NUM_PROXIES = 1;
   private static final int NUM_ROUTERS = 1;
 
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithSniProxy.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithSniProxy.java
index 030a064..d11bfa1 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithSniProxy.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithSniProxy.java
@@ -43,9 +43,9 @@ public class ClientServerTopologyWithSniProxy extends Topology {
   public static final String WITH_SNI_PROXY_PROPERTY = "benchmark.withSniProxy";
   public static final String WITH_SNI_PROXY_IMAGE_PROPERTY = "benchmark.withSniProxyImage";
 
-  private static final int NUM_LOCATORS = 1;
-  private static final int NUM_SERVERS = 2;
-  private static final int NUM_CLIENTS = 1;
+  private static final int NUM_LOCATORS = Integer.getInteger(WITH_LOCATOR_COUNT_PROPERTY, 1);
+  private static final int NUM_SERVERS = Integer.getInteger(WITH_SERVER_COUNT_PROPERTY, 2);
+  private static final int NUM_CLIENTS = Integer.getInteger(WITH_CLIENT_COUNT_PROPERTY, 1);
   private static final int NUM_PROXIES = 1;
 
   public enum SniProxyImplementation {
@@ -53,10 +53,9 @@ public class ClientServerTopologyWithSniProxy extends Topology {
     HAProxy,
     Envoy;
 
-    public static SniProxyImplementation valueOfIgnoreCase(String name) {
-      name = name.toLowerCase();
+    public static SniProxyImplementation valueOfIgnoreCase(final String name) {
       for (SniProxyImplementation sniProxyImplementation : SniProxyImplementation.values()) {
-        if (sniProxyImplementation.name().toLowerCase().equals(name)) {
+        if (sniProxyImplementation.name().equalsIgnoreCase(name)) {
           return sniProxyImplementation;
         }
       }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/P2pTopology.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/P2pTopology.java
index 0242bdb..56abe54 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/P2pTopology.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/P2pTopology.java
@@ -31,8 +31,8 @@ import org.apache.geode.benchmark.tasks.StopServer;
 import org.apache.geode.perftest.TestConfig;
 
 public class P2pTopology extends Topology {
-  private static final int NUM_LOCATORS = 1;
-  private static final int NUM_SERVERS = 3;
+  private static final int NUM_LOCATORS = Integer.getInteger(WITH_LOCATOR_COUNT_PROPERTY, 1);
+  private static final int NUM_SERVERS = Integer.getInteger(WITH_SERVER_COUNT_PROPERTY, 3);
 
   public static void configure(TestConfig config) {
     role(config, LOCATOR, NUM_LOCATORS);
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/Ports.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/Ports.java
index 41da7f1..0dc90df 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/Ports.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/Ports.java
@@ -34,4 +34,10 @@ public class Ports {
    * The SNI proxy port.
    */
   public static final int SNI_PROXY_PORT = 15443;
+
+  /**
+   * Default Redis port.
+   */
+  public static final int REDIS_PORT = 6379;
+
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/Topology.java b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/Topology.java
index 992a8e4..28b825b 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/Topology.java
+++ b/geode-benchmarks/src/main/java/org/apache/geode/benchmark/topology/Topology.java
@@ -15,13 +15,16 @@
 
 package org.apache.geode.benchmark.topology;
 
+import static java.lang.Boolean.getBoolean;
 import static org.apache.geode.benchmark.parameters.Utils.addToTestConfig;
+import static org.apache.geode.benchmark.topology.Roles.SERVER;
 
 import org.apache.geode.benchmark.parameters.GcLoggingParameters;
 import org.apache.geode.benchmark.parameters.GcParameters;
 import org.apache.geode.benchmark.parameters.HeapParameters;
 import org.apache.geode.benchmark.parameters.JvmParameters;
 import org.apache.geode.benchmark.parameters.ProfilerParameters;
+import org.apache.geode.benchmark.parameters.SafepointLoggingParameters;
 import org.apache.geode.perftest.TestConfig;
 
 public abstract class Topology {
@@ -34,17 +37,28 @@ public abstract class Topology {
   public static final String WITH_SECURITY_MANAGER_PROPERTY = "benchmark.withSecurityManager";
   static final String WITH_SECURITY_MANAGER_ARGUMENT = "-Dbenchmark.withSecurityManager=true";
 
-  static void configureCommon(TestConfig config) {
+  public static final String WITH_LOCATOR_COUNT_PROPERTY = "benchmark.withLocatorCount";
+  public static final String WITH_SERVER_COUNT_PROPERTY = "benchmark.withServerCount";
+  public static final String WITH_CLIENT_COUNT_PROPERTY = "benchmark.withClientCount";
+  public static final String WITH_ASYNC_REPLICATION = "benchmark.withAsyncReplication";
+
+  protected static void configureCommon(TestConfig config) {
     JvmParameters.configure(config);
     HeapParameters.configure(config);
     GcLoggingParameters.configure(config);
     GcParameters.configure(config);
+    SafepointLoggingParameters.configure(config);
     ProfilerParameters.configure(config);
 
     addToTestConfig(config, WITH_SSL_PROPERTY, WITH_SSL_ARGUMENT);
     addToTestConfig(config, WITH_SSL_PROTOCOLS_PROPERTY);
     addToTestConfig(config, WITH_SSL_CIPHERS_PROPERTY);
     addToTestConfig(config, WITH_SECURITY_MANAGER_PROPERTY, WITH_SECURITY_MANAGER_ARGUMENT);
+
+    if (getBoolean(WITH_ASYNC_REPLICATION)) {
+      config.jvmArgs(SERVER.name(), "-Dgemfire.disablePartitionedRegionBucketAck=true");
+    }
+
   }
 
 }
diff --git a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/Constants.java
similarity index 73%
copy from geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java
copy to geode-benchmarks/src/test/java/org/apache/geode/benchmark/Constants.java
index 922ff51..056d082 100644
--- a/geode-benchmarks/src/main/java/org/apache/geode/benchmark/parameters/GcImplementation.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/Constants.java
@@ -13,8 +13,12 @@
  * the License.
  */
 
-package org.apache.geode.benchmark.parameters;
+package org.apache.geode.benchmark;
 
-public enum GcImplementation {
-  CMS, G1, Z, Shenandoah;
+public interface Constants {
+  String JAVA_RUNTIME_VERSION = "java.runtime.version";
+  String JAVA_VERSION_8 = "1.8.0_212-b03";
+  String JAVA_VERSION_11 = "11.0.4+11";
+  String JAVA_VERSION_12 = "12.0.2+10";
+  String JAVA_VERSION_13 = "13+33";
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/junit/CartesianSubclassSource.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/junit/CartesianSubclassSource.java
new file mode 100644
index 0000000..2e7212f
--- /dev/null
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/junit/CartesianSubclassSource.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.benchmark.junit;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.params.provider.ArgumentsSource;
+
+/**
+ * {@code }@CartesianSubclassSource} is a argument source subclass of the specified {@linkplain
+ * #value Class} for tests annotated with {@code @CartesianProductTest}.
+ */
+@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Repeatable(CartesianSubclassSource.CartesianSubclassSources.class)
+@ArgumentsSource(SubclassSourceProvider.class)
+public @interface CartesianSubclassSource {
+
+  /**
+   * The Class to find subclasses for.
+   */
+  Class<?> value();
+
+  /**
+   * Class Packages to accept. All by default.
+   */
+  String[] acceptPackages() default {};
+
+  @Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
+  @Retention(RetentionPolicy.RUNTIME)
+  @Documented
+  @interface CartesianSubclassSources {
+    CartesianSubclassSource[] value();
+  }
+}
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/junit/EnableIfClassExists.java
similarity index 51%
copy from geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionBenchmarkTest.java
copy to geode-benchmarks/src/test/java/org/apache/geode/benchmark/junit/EnableIfClassExists.java
index 6b4d1df..6bfc1b1 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/junit/EnableIfClassExists.java
@@ -12,23 +12,31 @@
  * or implied. See the License for the specific language governing permissions and limitations under
  * the License.
  */
-package org.apache.geode.benchmark.tests;
 
-import java.io.File;
+package org.apache.geode.benchmark.junit;
 
-import org.junit.jupiter.api.io.TempDir;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
-import org.apache.geode.benchmark.LongRange;
-import org.apache.geode.perftest.TestRunners;
+import org.junit.jupiter.api.extension.ExtendWith;
 
-public class PartitionedFunctionExecutionBenchmarkTest {
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Repeatable(EnableIfClassExists.EnableIfClassesExist.class)
+@ExtendWith(EnableIfClassExistsCondition.class)
+public @interface EnableIfClassExists {
+  String value();
 
-  @TempDir
-  File folder;
-
-  public void benchmarkRunsSuccessfully() throws Exception {
-    PartitionedFunctionExecutionBenchmark test = new PartitionedFunctionExecutionBenchmark();
-    test.setKeyRange(new LongRange(0, 100));
-    TestRunners.minimalRunner(folder).runTest(test);
+  @Target({ElementType.TYPE, ElementType.METHOD})
+  @Retention(RetentionPolicy.RUNTIME)
+  @Documented
+  @interface EnableIfClassesExist {
+    EnableIfClassExists[] value();
   }
+
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/junit/EnableIfClassExistsCondition.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/junit/EnableIfClassExistsCondition.java
new file mode 100644
index 0000000..ad42c74
--- /dev/null
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/junit/EnableIfClassExistsCondition.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.benchmark.junit;
+
+import static java.lang.String.format;
+import static org.junit.jupiter.api.extension.ConditionEvaluationResult.disabled;
+import static org.junit.jupiter.api.extension.ConditionEvaluationResult.enabled;
+import static org.junit.platform.commons.util.AnnotationUtils.findRepeatableAnnotations;
+
+import java.lang.reflect.AnnotatedElement;
+import java.util.Optional;
+
+import org.junit.jupiter.api.extension.ConditionEvaluationResult;
+import org.junit.jupiter.api.extension.ExecutionCondition;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+public class EnableIfClassExistsCondition implements ExecutionCondition {
+
+  public static final ConditionEvaluationResult ENABLED =
+      enabled("No @EnableIfClassExists conditions resulting in 'disabled' execution encountered");
+
+  @Override
+  public ConditionEvaluationResult evaluateExecutionCondition(final ExtensionContext context) {
+    Optional<AnnotatedElement> optionalElement = context.getElement();
+    if (optionalElement.isPresent()) {
+      AnnotatedElement annotatedElement = optionalElement.get();
+      return findRepeatableAnnotations(annotatedElement, EnableIfClassExists.class).stream()
+          .map(this::evaluate)
+          .filter(ConditionEvaluationResult::isDisabled)
+          .findFirst()
+          .orElse(ENABLED);
+    }
+    return ENABLED;
+  }
+
+  private ConditionEvaluationResult evaluate(final EnableIfClassExists enableIfClassExists) {
+    final String className = enableIfClassExists.value();
+    try {
+      Class.forName(className);
+      return enabled(format("Class [%s] exists.", className));
+    } catch (ClassNotFoundException e) {
+      return disabled(format("Class [%s] does not exist.", className));
+    }
+  }
+}
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/junit/SubclassSourceProvider.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/junit/SubclassSourceProvider.java
new file mode 100644
index 0000000..e82b2e6
--- /dev/null
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/junit/SubclassSourceProvider.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.benchmark.junit;
+
+import java.util.List;
+import java.util.stream.Stream;
+
+import io.github.classgraph.ClassGraph;
+import io.github.classgraph.ScanResult;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+import org.junitpioneer.jupiter.CartesianAnnotationConsumer;
+
+public class SubclassSourceProvider
+    implements CartesianAnnotationConsumer<CartesianSubclassSource>, ArgumentsProvider {
+
+  private List<Class<?>> subclasses;
+
+  @Override
+  public void accept(final CartesianSubclassSource subclassSource) {
+    try (final ScanResult scanResult = new ClassGraph().enableAllInfo()
+        .acceptPackages(subclassSource.acceptPackages())
+        .scan()) {
+      subclasses = scanResult.getSubclasses(subclassSource.value().getName()).loadClasses();
+    }
+  }
+
+  @Override
+  public Stream<? extends Arguments> provideArguments(final ExtensionContext context) {
+    return subclasses.stream().map(Arguments::of);
+  }
+}
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/parameters/GcLoggingParametersTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/parameters/GcLoggingParametersTest.java
index f1caa05..56b1a12 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/parameters/GcLoggingParametersTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/parameters/GcLoggingParametersTest.java
@@ -15,89 +15,85 @@
 
 package org.apache.geode.benchmark.parameters;
 
-import static org.apache.geode.benchmark.topology.Roles.CLIENT;
-import static org.apache.geode.benchmark.topology.Roles.LOCATOR;
-import static org.apache.geode.benchmark.topology.Roles.SERVER;
+import static org.apache.geode.benchmark.Constants.JAVA_RUNTIME_VERSION;
+import static org.apache.geode.benchmark.Constants.JAVA_VERSION_11;
+import static org.apache.geode.benchmark.Constants.JAVA_VERSION_12;
+import static org.apache.geode.benchmark.Constants.JAVA_VERSION_13;
+import static org.apache.geode.benchmark.Constants.JAVA_VERSION_8;
+import static org.apache.geode.benchmark.parameters.GcLoggingParameters.WITH_GC_LOGGING;
+import static org.apache.geode.benchmark.topology.RoleKinds.GEODE_PRODUCT;
+import static org.apache.geode.benchmark.topology.Roles.rolesFor;
 import static org.assertj.core.api.Assertions.assertThat;
 
-import java.util.Properties;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.ClearSystemProperty;
+import org.junitpioneer.jupiter.SetSystemProperty;
 
 import org.apache.geode.perftest.TestConfig;
 
 class GcLoggingParametersTest {
 
-  private static final String JAVA_RUNTIME_VERSION = "java.runtime.version";
-
-  private Properties systemProperties;
-
-  @BeforeEach
-  public void beforeEach() {
-    systemProperties = (Properties) System.getProperties().clone();
-  }
+  @Test
+  @ClearSystemProperty(key = WITH_GC_LOGGING)
+  public void withDefault() {
+    final TestConfig testConfig = new TestConfig();
+    Utils.configureGeodeProductJvms(testConfig, "-mockArg");
+    GcLoggingParameters.configure(testConfig);
 
-  @AfterEach
-  public void afterEach() {
-    System.setProperties(systemProperties);
+    rolesFor(GEODE_PRODUCT).forEach(role -> {
+      assertThat(testConfig.getJvmArgs().get(role.name())).doesNotContainSubsequence("-Xlog:gc");
+      assertThat(testConfig.getJvmArgs().get(role.name())).doesNotContainSubsequence("-Xloggc");
+    });
   }
 
   @Test
+  @SetSystemProperty(key = WITH_GC_LOGGING, value = "true")
+  @SetSystemProperty(key = JAVA_RUNTIME_VERSION, value = JAVA_VERSION_8)
   public void withJava8() {
-    System.setProperty(JAVA_RUNTIME_VERSION, "1.8.0_212-b03");
     final TestConfig testConfig = new TestConfig();
     GcLoggingParameters.configure(testConfig);
     assertThatJava8GcLog(testConfig);
   }
 
   @Test
+  @SetSystemProperty(key = WITH_GC_LOGGING, value = "true")
+  @SetSystemProperty(key = JAVA_RUNTIME_VERSION, value = JAVA_VERSION_11)
   public void withJava11() {
-    System.setProperty(JAVA_RUNTIME_VERSION, "11.0.4+11");
     final TestConfig testConfig = new TestConfig();
     GcLoggingParameters.configure(testConfig);
     assertThatJava9GcLog(testConfig);
   }
 
   @Test
+  @SetSystemProperty(key = WITH_GC_LOGGING, value = "true")
+  @SetSystemProperty(key = JAVA_RUNTIME_VERSION, value = JAVA_VERSION_12)
   public void withJava12() {
-    System.setProperty(JAVA_RUNTIME_VERSION, "12.0.2+10");
     final TestConfig testConfig = new TestConfig();
     GcLoggingParameters.configure(testConfig);
     assertThatJava9GcLog(testConfig);
   }
 
   @Test
+  @SetSystemProperty(key = WITH_GC_LOGGING, value = "true")
+  @SetSystemProperty(key = JAVA_RUNTIME_VERSION, value = JAVA_VERSION_13)
   public void withJava13() {
-    System.setProperty(JAVA_RUNTIME_VERSION, "13+33");
     final TestConfig testConfig = new TestConfig();
     GcLoggingParameters.configure(testConfig);
     assertThatJava9GcLog(testConfig);
   }
 
   private void assertThatJava8GcLog(TestConfig testConfig) {
-    assertThat(testConfig.getJvmArgs().get(CLIENT.name())).contains("-Xloggc:OUTPUT_DIR/gc.log");
-    assertThat(testConfig.getJvmArgs().get(SERVER.name())).contains("-Xloggc:OUTPUT_DIR/gc.log");
-    assertThat(testConfig.getJvmArgs().get(LOCATOR.name())).contains("-Xloggc:OUTPUT_DIR/gc.log");
-    assertThat(testConfig.getJvmArgs().get(CLIENT.name()))
-        .doesNotContain("-Xlog:gc:OUTPUT_DIR/gc.log");
-    assertThat(testConfig.getJvmArgs().get(SERVER.name()))
-        .doesNotContain("-Xlog:gc:OUTPUT_DIR/gc.log");
-    assertThat(testConfig.getJvmArgs().get(LOCATOR.name()))
-        .doesNotContain("-Xlog:gc:OUTPUT_DIR/gc.log");
+    rolesFor(GEODE_PRODUCT).forEach(role -> {
+      assertThat(testConfig.getJvmArgs().get(role.name())).contains("-Xloggc:OUTPUT_DIR/gc.log");
+      assertThat(testConfig.getJvmArgs().get(role.name())).doesNotContainSubsequence("-Xlog:gc");
+    });
   }
 
   private void assertThatJava9GcLog(TestConfig testConfig) {
-    assertThat(testConfig.getJvmArgs().get(CLIENT.name())).contains("-Xlog:gc*:OUTPUT_DIR/gc.log");
-    assertThat(testConfig.getJvmArgs().get(SERVER.name())).contains("-Xlog:gc*:OUTPUT_DIR/gc.log");
-    assertThat(testConfig.getJvmArgs().get(LOCATOR.name())).contains("-Xlog:gc*:OUTPUT_DIR/gc.log");
-    assertThat(testConfig.getJvmArgs().get(CLIENT.name()))
-        .doesNotContain("-Xloggc:OUTPUT_DIR/gc.log");
-    assertThat(testConfig.getJvmArgs().get(SERVER.name()))
-        .doesNotContain("-Xloggc:OUTPUT_DIR/gc.log");
-    assertThat(testConfig.getJvmArgs().get(LOCATOR.name()))
-        .doesNotContain("-Xloggc:OUTPUT_DIR/gc.log");
+    rolesFor(GEODE_PRODUCT).forEach(role -> {
+      assertThat(testConfig.getJvmArgs().get(role.name())).contains("-Xlog:gc*:OUTPUT_DIR/gc.log");
+      assertThat(testConfig.getJvmArgs().get(role.name())).doesNotContainSubsequence("-Xloggc");
+    });
   }
 
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/parameters/GcParametersTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/parameters/GcParametersTest.java
index 5003187..db65933 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/parameters/GcParametersTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/parameters/GcParametersTest.java
@@ -15,121 +15,124 @@
 
 package org.apache.geode.benchmark.parameters;
 
-import static org.apache.geode.benchmark.topology.Roles.CLIENT;
-import static org.apache.geode.benchmark.topology.Roles.LOCATOR;
-import static org.apache.geode.benchmark.topology.Roles.SERVER;
+import static org.apache.geode.benchmark.Constants.JAVA_RUNTIME_VERSION;
+import static org.apache.geode.benchmark.Constants.JAVA_VERSION_11;
+import static org.apache.geode.benchmark.Constants.JAVA_VERSION_8;
+import static org.apache.geode.benchmark.topology.RoleKinds.GEODE_PRODUCT;
+import static org.apache.geode.benchmark.topology.Roles.rolesFor;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
-import java.util.Properties;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.ClearSystemProperty;
+import org.junitpioneer.jupiter.SetSystemProperty;
 
 import org.apache.geode.perftest.TestConfig;
 
 class GcParametersTest {
   private static final String WITH_GC = "benchmark.withGc";
-  private static final String JAVA_RUNTIME_VERSION = "java.runtime.version";
-  private static final String XX_USE_ZGC = "-XX:+UseZGC";
-  private static final String XX_USE_G_1_GC = "-XX:+UseG1GC";
-  private static final String XX_USE_CONC_MARK_SWEEP_GC = "-XX:+UseConcMarkSweepGC";
-
-  private Properties systemProperties;
-
-  @BeforeEach
-  public void beforeEach() {
-    systemProperties = (Properties) System.getProperties().clone();
-  }
-
-  @AfterEach
-  public void afterEach() {
-    System.setProperties(systemProperties);
-  }
+  private static final String XX_UseZGC = "-XX:+UseZGC";
+  private static final String XX_UseG1GC = "-XX:+UseG1GC";
+  private static final String XX_UseConcMarkSweepGC = "-XX:+UseConcMarkSweepGC";
+  private static final String XX_UseShenandoahGC = "-XX:+UseShenandoahGC";
+  private static final String XX_UseEpsilonGC = "-XX:+UseEpsilonGC";
 
   @Test
+  @ClearSystemProperty(key = WITH_GC)
   public void withDefault() {
-    System.clearProperty(WITH_GC);
     final TestConfig testConfig = new TestConfig();
     GcParameters.configure(testConfig);
     assertCms(testConfig);
   }
 
   @Test
+  @SetSystemProperty(key = WITH_GC, value = "CMS")
   public void withCms() {
-    System.setProperty(WITH_GC, "CMS");
     final TestConfig testConfig = new TestConfig();
     GcParameters.configure(testConfig);
     assertCms(testConfig);
   }
 
   @Test
+  @SetSystemProperty(key = WITH_GC, value = "G1")
   public void withG1() {
-    System.setProperty(WITH_GC, "G1");
     final TestConfig testConfig = new TestConfig();
     GcParameters.configure(testConfig);
     assertG1(testConfig);
   }
 
   @Test
+  @SetSystemProperty(key = WITH_GC, value = "Z")
+  @SetSystemProperty(key = JAVA_RUNTIME_VERSION, value = JAVA_VERSION_11)
   public void withZ() {
-    System.setProperty(WITH_GC, "Z");
-    System.setProperty(JAVA_RUNTIME_VERSION, "11.0.4+11");
     final TestConfig testConfig = new TestConfig();
     GcParameters.configure(testConfig);
     assertZ(testConfig);
   }
 
   @Test
+  @SetSystemProperty(key = WITH_GC, value = "Z")
+  @SetSystemProperty(key = JAVA_RUNTIME_VERSION, value = JAVA_VERSION_8)
   public void withZinJava8() {
-    System.setProperty(WITH_GC, "Z");
-    System.setProperty(JAVA_RUNTIME_VERSION, "1.8.0_212-b03");
     final TestConfig testConfig = new TestConfig();
     assertThatThrownBy(() -> GcParameters.configure(testConfig))
         .isInstanceOf(IllegalArgumentException.class);
   }
 
+  @Test
+  @SetSystemProperty(key = WITH_GC, value = "Shenandoah")
+  public void withShenandoah() {
+    final TestConfig testConfig = new TestConfig();
+    GcParameters.configure(testConfig);
+    assertShenandoah(testConfig);
+  }
+
+  @Test
+  @SetSystemProperty(key = WITH_GC, value = "Epsilon")
+  public void withEpsilon() {
+    final TestConfig testConfig = new TestConfig();
+    GcParameters.configure(testConfig);
+    assertEpsilon(testConfig);
+  }
+
   private void assertCms(TestConfig testConfig) {
-    assertThat(testConfig.getJvmArgs().get(CLIENT.name())).contains(XX_USE_CONC_MARK_SWEEP_GC);
-    assertThat(testConfig.getJvmArgs().get(SERVER.name())).contains(XX_USE_CONC_MARK_SWEEP_GC);
-    assertThat(testConfig.getJvmArgs().get(LOCATOR.name())).contains(XX_USE_CONC_MARK_SWEEP_GC);
-    assertThat(testConfig.getJvmArgs().get(CLIENT.name())).doesNotContain(XX_USE_G_1_GC);
-    assertThat(testConfig.getJvmArgs().get(SERVER.name())).doesNotContain(XX_USE_G_1_GC);
-    assertThat(testConfig.getJvmArgs().get(LOCATOR.name())).doesNotContain(XX_USE_G_1_GC);
-    assertThat(testConfig.getJvmArgs().get(CLIENT.name())).doesNotContain(XX_USE_ZGC);
-    assertThat(testConfig.getJvmArgs().get(SERVER.name())).doesNotContain(XX_USE_ZGC);
-    assertThat(testConfig.getJvmArgs().get(LOCATOR.name())).doesNotContain(XX_USE_ZGC);
+    rolesFor(GEODE_PRODUCT).forEach(role -> {
+      assertThat(testConfig.getJvmArgs().get(role.name())).contains(XX_UseConcMarkSweepGC);
+      assertThat(testConfig.getJvmArgs().get(role.name())).doesNotContain(XX_UseG1GC);
+      assertThat(testConfig.getJvmArgs().get(role.name())).doesNotContain(XX_UseZGC);
+    });
   }
 
   private void assertG1(TestConfig testConfig) {
-    assertThat(testConfig.getJvmArgs().get(CLIENT.name())).contains(XX_USE_G_1_GC);
-    assertThat(testConfig.getJvmArgs().get(SERVER.name())).contains(XX_USE_G_1_GC);
-    assertThat(testConfig.getJvmArgs().get(LOCATOR.name())).contains(XX_USE_G_1_GC);
-    assertThat(testConfig.getJvmArgs().get(CLIENT.name()))
-        .doesNotContain(XX_USE_CONC_MARK_SWEEP_GC);
-    assertThat(testConfig.getJvmArgs().get(SERVER.name()))
-        .doesNotContain(XX_USE_CONC_MARK_SWEEP_GC);
-    assertThat(testConfig.getJvmArgs().get(LOCATOR.name()))
-        .doesNotContain(XX_USE_CONC_MARK_SWEEP_GC);
-    assertThat(testConfig.getJvmArgs().get(CLIENT.name())).doesNotContain(XX_USE_ZGC);
-    assertThat(testConfig.getJvmArgs().get(SERVER.name())).doesNotContain(XX_USE_ZGC);
-    assertThat(testConfig.getJvmArgs().get(LOCATOR.name())).doesNotContain(XX_USE_ZGC);
+    rolesFor(GEODE_PRODUCT).forEach(role -> {
+      assertThat(testConfig.getJvmArgs().get(role.name())).contains(XX_UseG1GC);
+      assertThat(testConfig.getJvmArgs().get(role.name())).doesNotContain(XX_UseConcMarkSweepGC);
+      assertThat(testConfig.getJvmArgs().get(role.name())).doesNotContain(XX_UseZGC);
+    });
   }
 
   private void assertZ(TestConfig testConfig) {
-    assertThat(testConfig.getJvmArgs().get(CLIENT.name())).contains(XX_USE_ZGC);
-    assertThat(testConfig.getJvmArgs().get(SERVER.name())).contains(XX_USE_ZGC);
-    assertThat(testConfig.getJvmArgs().get(LOCATOR.name())).contains(XX_USE_ZGC);
-    assertThat(testConfig.getJvmArgs().get(CLIENT.name()))
-        .doesNotContain(XX_USE_CONC_MARK_SWEEP_GC);
-    assertThat(testConfig.getJvmArgs().get(SERVER.name()))
-        .doesNotContain(XX_USE_CONC_MARK_SWEEP_GC);
-    assertThat(testConfig.getJvmArgs().get(LOCATOR.name()))
-        .doesNotContain(XX_USE_CONC_MARK_SWEEP_GC);
-    assertThat(testConfig.getJvmArgs().get(CLIENT.name())).doesNotContain(XX_USE_G_1_GC);
-    assertThat(testConfig.getJvmArgs().get(SERVER.name())).doesNotContain(XX_USE_G_1_GC);
-    assertThat(testConfig.getJvmArgs().get(LOCATOR.name())).doesNotContain(XX_USE_G_1_GC);
+    rolesFor(GEODE_PRODUCT).forEach(role -> {
+      assertThat(testConfig.getJvmArgs().get(role.name())).contains(XX_UseZGC);
+      assertThat(testConfig.getJvmArgs().get(role.name())).doesNotContain(XX_UseConcMarkSweepGC);
+      assertThat(testConfig.getJvmArgs().get(role.name())).doesNotContain(XX_UseG1GC);
+    });
+  }
+
+  private void assertShenandoah(TestConfig testConfig) {
+    rolesFor(GEODE_PRODUCT).forEach(role -> {
+      assertThat(testConfig.getJvmArgs().get(role.name())).contains(XX_UseShenandoahGC);
+      assertThat(testConfig.getJvmArgs().get(role.name())).doesNotContain(XX_UseConcMarkSweepGC);
+      assertThat(testConfig.getJvmArgs().get(role.name())).doesNotContain(XX_UseG1GC);
+    });
+  }
+
+  private void assertEpsilon(TestConfig testConfig) {
+    rolesFor(GEODE_PRODUCT).forEach(role -> {
+      assertThat(testConfig.getJvmArgs().get(role.name())).contains(XX_UseEpsilonGC);
+      assertThat(testConfig.getJvmArgs().get(role.name())).doesNotContain(XX_UseConcMarkSweepGC);
+      assertThat(testConfig.getJvmArgs().get(role.name())).doesNotContain(XX_UseG1GC);
+    });
   }
 
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/parameters/HeapParametersTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/parameters/HeapParametersTest.java
index 332fc43..8c2f1e2 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/parameters/HeapParametersTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/parameters/HeapParametersTest.java
@@ -15,16 +15,13 @@
 
 package org.apache.geode.benchmark.parameters;
 
-import static org.apache.geode.benchmark.topology.Roles.CLIENT;
-import static org.apache.geode.benchmark.topology.Roles.LOCATOR;
-import static org.apache.geode.benchmark.topology.Roles.SERVER;
+import static org.apache.geode.benchmark.topology.RoleKinds.GEODE_PRODUCT;
+import static org.apache.geode.benchmark.topology.Roles.rolesFor;
 import static org.assertj.core.api.Assertions.assertThat;
 
-import java.util.Properties;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.ClearSystemProperty;
+import org.junitpioneer.jupiter.SetSystemProperty;
 
 import org.apache.geode.perftest.TestConfig;
 
@@ -32,40 +29,26 @@ class HeapParametersTest {
 
   private static final String WITH_HEAP = "benchmark.withHeap";
 
-  private Properties systemProperties;
-
-  @BeforeEach
-  public void beforeEach() {
-    systemProperties = (Properties) System.getProperties().clone();
-  }
-
-  @AfterEach
-  public void afterEach() {
-    System.setProperties(systemProperties);
-  }
-
   @Test
+  @ClearSystemProperty(key = WITH_HEAP)
   public void withDefault() {
-    System.clearProperty(WITH_HEAP);
     final TestConfig testConfig = new TestConfig();
     HeapParameters.configure(testConfig);
     assertHeap(testConfig, "8g");
   }
 
   @Test
+  @SetSystemProperty(key = WITH_HEAP, value = "16g")
   public void with16g() {
-    System.setProperty(WITH_HEAP, "16g");
     final TestConfig testConfig = new TestConfig();
     HeapParameters.configure(testConfig);
     assertHeap(testConfig, "16g");
   }
 
   private void assertHeap(final TestConfig testConfig, final String heap) {
-    assertThat(testConfig.getJvmArgs().get(CLIENT.name())).contains("-Xmx" + heap);
-    assertThat(testConfig.getJvmArgs().get(SERVER.name())).contains("-Xmx" + heap);
-    assertThat(testConfig.getJvmArgs().get(LOCATOR.name())).contains("-Xmx" + heap);
-    assertThat(testConfig.getJvmArgs().get(CLIENT.name())).contains("-Xms" + heap);
-    assertThat(testConfig.getJvmArgs().get(SERVER.name())).contains("-Xms" + heap);
-    assertThat(testConfig.getJvmArgs().get(LOCATOR.name())).contains("-Xms" + heap);
+    rolesFor(GEODE_PRODUCT).forEach(role -> {
+      assertThat(testConfig.getJvmArgs().get(role.name())).contains("-Xmx" + heap);
+      assertThat(testConfig.getJvmArgs().get(role.name())).contains("-Xms" + heap);
+    });
   }
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/parameters/SafepointLoggingParametersTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/parameters/SafepointLoggingParametersTest.java
new file mode 100644
index 0000000..55ff691
--- /dev/null
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/parameters/SafepointLoggingParametersTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.benchmark.parameters;
+
+import static org.apache.geode.benchmark.Constants.JAVA_RUNTIME_VERSION;
+import static org.apache.geode.benchmark.Constants.JAVA_VERSION_11;
+import static org.apache.geode.benchmark.Constants.JAVA_VERSION_8;
+import static org.apache.geode.benchmark.parameters.SafepointLoggingParameters.WITH_SAFEPOINT_LOGGING;
+import static org.apache.geode.benchmark.parameters.SafepointLoggingParameters.XLOG_SAFEPOINT;
+import static org.apache.geode.benchmark.topology.RoleKinds.GEODE_PRODUCT;
+import static org.apache.geode.benchmark.topology.Roles.rolesFor;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.ClearSystemProperty;
+import org.junitpioneer.jupiter.SetSystemProperty;
+
+import org.apache.geode.perftest.TestConfig;
+
+class SafepointLoggingParametersTest {
+
+  @Test
+  @ClearSystemProperty(key = WITH_SAFEPOINT_LOGGING)
+  public void withDefault() {
+    final TestConfig testConfig = new TestConfig();
+    Utils.configureGeodeProductJvms(testConfig, "-mockArg");
+    SafepointLoggingParameters.configure(testConfig);
+
+    rolesFor(GEODE_PRODUCT).forEach(role -> {
+      assertThat(testConfig.getJvmArgs().get(role.name())).doesNotContain(XLOG_SAFEPOINT);
+    });
+
+  }
+
+  @Test
+  @SetSystemProperty(key = WITH_SAFEPOINT_LOGGING, value = "true")
+  @SetSystemProperty(key = JAVA_RUNTIME_VERSION, value = JAVA_VERSION_11)
+  public void withSafepointLoggingJava11() {
+    final TestConfig testConfig = new TestConfig();
+    SafepointLoggingParameters.configure(testConfig);
+
+    rolesFor(GEODE_PRODUCT).forEach(role -> {
+      assertThat(testConfig.getJvmArgs().get(role.name())).contains(XLOG_SAFEPOINT);
+    });
+  }
+
+  @Test
+  @SetSystemProperty(key = WITH_SAFEPOINT_LOGGING, value = "true")
+  @SetSystemProperty(key = JAVA_RUNTIME_VERSION, value = JAVA_VERSION_8)
+  public void withSafepointLoggingJava8() {
+    final TestConfig testConfig = new TestConfig();
+    Utils.configureGeodeProductJvms(testConfig, "-mockArg");
+    SafepointLoggingParameters.configure(testConfig);
+
+    rolesFor(GEODE_PRODUCT).forEach(role -> {
+      assertThat(testConfig.getJvmArgs().get(role.name())).doesNotContain(XLOG_SAFEPOINT);
+    });
+  }
+
+}
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/redis/tests/RedisBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/redis/tests/RedisBenchmarkTest.java
new file mode 100644
index 0000000..3cbc5cd
--- /dev/null
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/redis/tests/RedisBenchmarkTest.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.benchmark.redis.tests;
+
+import static java.lang.System.setProperty;
+import static org.apache.geode.benchmark.redis.tests.RedisBenchmark.WITH_REDIS_CLIENT_PROPERTY;
+import static org.apache.geode.benchmark.topology.Topology.WITH_SERVER_COUNT_PROPERTY;
+
+import java.io.File;
+
+import org.junit.jupiter.api.io.TempDir;
+import org.junitpioneer.jupiter.CartesianEnumSource;
+import org.junitpioneer.jupiter.CartesianProductTest;
+import org.junitpioneer.jupiter.ClearSystemProperty;
+import org.junitpioneer.jupiter.SetSystemProperty;
+
+import org.apache.geode.benchmark.LongRange;
+import org.apache.geode.benchmark.junit.CartesianSubclassSource;
+import org.apache.geode.benchmark.junit.EnableIfClassExists;
+import org.apache.geode.benchmark.redis.tests.RedisBenchmark.RedisClientImplementation;
+import org.apache.geode.perftest.TestRunners;
+
+@EnableIfClassExists("org.apache.geode.redis.internal.GeodeRedisServer")
+public class RedisBenchmarkTest {
+
+  @TempDir()
+  File folder;
+
+  @CartesianProductTest()
+  @CartesianEnumSource(RedisClientImplementation.class)
+  @CartesianSubclassSource(RedisBenchmark.class)
+  @ClearSystemProperty(key = WITH_REDIS_CLIENT_PROPERTY)
+  @SetSystemProperty(key = WITH_SERVER_COUNT_PROPERTY, value = "1")
+  public void benchmarkRunsSuccessfully(final RedisClientImplementation redisClientImplementation,
+      final Class<? extends RedisBenchmark> redisBenchmark)
+      throws Exception {
+    setProperty(WITH_REDIS_CLIENT_PROPERTY, redisClientImplementation.name());
+
+    final RedisBenchmark test = redisBenchmark.newInstance();
+    test.setKeyRange(new LongRange(0, 100));
+    test.setValidationEnabled(true);
+
+    TestRunners.minimalRunner(folder).runTest(test);
+  }
+}
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/redis/topology/GeodeTopologyTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/redis/topology/GeodeTopologyTest.java
new file mode 100644
index 0000000..455bf7d
--- /dev/null
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/redis/topology/GeodeTopologyTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.benchmark.redis.topology;
+
+import static org.apache.geode.benchmark.redis.parameters.NettyParameters.WITH_NETTY_THREADS;
+import static org.apache.geode.benchmark.tests.GeodeBenchmark.WITH_BUCKETS;
+import static org.apache.geode.benchmark.tests.GeodeBenchmark.WITH_REPLICAS;
+import static org.apache.geode.benchmark.topology.Roles.SERVER;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.ClearSystemProperty;
+import org.junitpioneer.jupiter.SetSystemProperty;
+
+import org.apache.geode.perftest.TestConfig;
+
+public class GeodeTopologyTest {
+
+  @Test
+  @ClearSystemProperty(key = WITH_REPLICAS)
+  @ClearSystemProperty(key = WITH_BUCKETS)
+  @ClearSystemProperty(key = WITH_NETTY_THREADS)
+  public void setsDefaultJvmArgs() {
+    final TestConfig testConfig = new TestConfig();
+    GeodeTopology.configure(testConfig);
+
+    assertThat(testConfig.getJvmArgs().get(SERVER.name()))
+        .contains("-Denable-unsupported-commands=true", "-Dredis.replicas=1",
+            "-Dredis.region.buckets=128", "-Djava.lang.Integer.IntegerCache.high=128")
+        .doesNotContainSubsequence("-Dio.netty.eventLoopThreads=");
+  }
+
+  @Test
+  @SetSystemProperty(key = WITH_REPLICAS, value = "3")
+  public void setsReplicas() {
+    final TestConfig testConfig = new TestConfig();
+    GeodeTopology.configure(testConfig);
+
+    assertThat(testConfig.getJvmArgs().get(SERVER.name())).contains("-Dredis.replicas=3");
+  }
+
+  @Test
+  @SetSystemProperty(key = WITH_BUCKETS, value = "123")
+  public void setsBuckets() {
+    final TestConfig testConfig = new TestConfig();
+    GeodeTopology.configure(testConfig);
+
+    assertThat(testConfig.getJvmArgs().get(SERVER.name()))
+        .contains("-Dredis.region.buckets=123", "-Djava.lang.Integer.IntegerCache.high=123");
+  }
+
+  @Test
+  @SetSystemProperty(key = WITH_NETTY_THREADS, value = "3")
+  public void setsNettyThreads() {
+    final TestConfig testConfig = new TestConfig();
+    GeodeTopology.configure(testConfig);
+
+    assertThat(testConfig.getJvmArgs().get(SERVER.name()))
+        .contains("-Dio.netty.eventLoopThreads=3");
+  }
+
+}
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/redis/topology/ManualRedisTopologyTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/redis/topology/ManualRedisTopologyTest.java
new file mode 100644
index 0000000..44da9e7
--- /dev/null
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/redis/topology/ManualRedisTopologyTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.benchmark.redis.topology;
+
+import static java.net.InetSocketAddress.createUnresolved;
+import static org.apache.geode.benchmark.redis.topology.ManualRedisTopology.WITH_REDIS_SERVERS_PROPERTY;
+import static org.apache.geode.benchmark.topology.Ports.REDIS_PORT;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.ClearSystemProperty;
+import org.junitpioneer.jupiter.SetSystemProperty;
+
+import org.apache.geode.benchmark.redis.tasks.InitRedisServersAttribute;
+import org.apache.geode.perftest.TestConfig;
+import org.apache.geode.perftest.TestStep;
+
+public class ManualRedisTopologyTest {
+
+  @Test
+  @SetSystemProperty(key = WITH_REDIS_SERVERS_PROPERTY, value = "a")
+  public void configureWithOneServer() {
+    final TestConfig testConfig = new TestConfig();
+    ManualRedisTopology.configure(testConfig);
+    assertThat(testConfig.getBefore().stream().map(TestStep::getTask)
+        .filter(InitRedisServersAttribute.class::isInstance)
+        .map(InitRedisServersAttribute.class::cast)
+        .findFirst()).hasValueSatisfying(t -> assertThat(t.getServers()).containsExactly(
+            createUnresolved("a", REDIS_PORT)));
+  }
+
+  @Test
+  @SetSystemProperty(key = WITH_REDIS_SERVERS_PROPERTY, value = "a;b;c")
+  public void configureWithMultipleServer() {
+    final TestConfig testConfig = new TestConfig();
+    ManualRedisTopology.configure(testConfig);
+    assertThat(testConfig.getBefore().stream().map(TestStep::getTask)
+        .filter(InitRedisServersAttribute.class::isInstance)
+        .map(InitRedisServersAttribute.class::cast)
+        .findFirst()).hasValueSatisfying(t -> assertThat(t.getServers()).containsExactly(
+            createUnresolved("a", REDIS_PORT), createUnresolved("b", REDIS_PORT),
+            createUnresolved("c", REDIS_PORT)));
+  }
+
+  @Test
+  @ClearSystemProperty(key = WITH_REDIS_SERVERS_PROPERTY)
+  public void configureWithNoServersThrows() {
+    final TestConfig testConfig = new TestConfig();
+    assertThatThrownBy(() -> ManualRedisTopology.configure(testConfig))
+        .isInstanceOf(IllegalArgumentException.class);
+  }
+
+  @Test
+  @SetSystemProperty(key = WITH_REDIS_SERVERS_PROPERTY, value = "")
+  public void configureWithNoEmptyThrows() {
+    final TestConfig testConfig = new TestConfig();
+    assertThatThrownBy(() -> ManualRedisTopology.configure(testConfig))
+        .isInstanceOf(IllegalArgumentException.class);
+  }
+
+}
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/redis/topology/RedisTopologyTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/redis/topology/RedisTopologyTest.java
new file mode 100644
index 0000000..f83ef2a
--- /dev/null
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/redis/topology/RedisTopologyTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.apache.geode.benchmark.redis.topology;
+
+import static org.apache.geode.benchmark.tests.GeodeBenchmark.WITH_REPLICAS;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.ClearSystemProperty;
+import org.junitpioneer.jupiter.SetSystemProperty;
+
+import org.apache.geode.benchmark.redis.tasks.CreateRedisCluster;
+import org.apache.geode.perftest.TestConfig;
+import org.apache.geode.perftest.TestStep;
+
+public class RedisTopologyTest {
+
+  @Test
+  @ClearSystemProperty(key = WITH_REPLICAS)
+  public void createRedisClusterDefaultReplicas() {
+    final TestConfig testConfig = new TestConfig();
+    RedisTopology.configure(testConfig);
+    assertThat(testConfig.getBefore().stream().map(TestStep::getTask)
+        .filter(CreateRedisCluster.class::isInstance).map(CreateRedisCluster.class::cast)
+        .findFirst()).hasValueSatisfying(t -> assertThat(t.getReplicas()).isEqualTo(1));
+  }
+
+  @Test
+  @SetSystemProperty(key = WITH_REPLICAS, value = "3")
+  public void createRedisClusterSetsReplicas() {
+    final TestConfig testConfig = new TestConfig();
+    RedisTopology.configure(testConfig);
+    assertThat(testConfig.getBefore().stream().map(TestStep::getTask)
+        .filter(CreateRedisCluster.class::isInstance).map(CreateRedisCluster.class::cast)
+        .findFirst()).hasValueSatisfying(t -> assertThat(t.getReplicas()).isEqualTo(3));
+  }
+
+}
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tasks/WeightedTasksTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tasks/WeightedTasksTest.java
new file mode 100644
index 0000000..0200601
--- /dev/null
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tasks/WeightedTasksTest.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.benchmark.tasks;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.data.Offset.offset;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockingDetails;
+import static org.mockito.Mockito.verify;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.junit.jupiter.api.Test;
+import org.yardstickframework.BenchmarkDriver;
+
+import org.apache.geode.benchmark.tasks.WeightedTasks.WeightedTask;
+
+class WeightedTasksTest {
+
+  @Test
+  public void eventuallyCallsBothTasks() throws Exception {
+    final BenchmarkDriver task1 = mock(BenchmarkDriver.class);
+    final BenchmarkDriver task2 = mock(BenchmarkDriver.class);
+
+    final Map<Object, Object> context = new ConcurrentHashMap<>();
+
+    final WeightedTasks weightedTasks =
+        new WeightedTasks(new WeightedTask(80, task1), new WeightedTask(20, task2));
+
+    while (mockingDetails(task1).getInvocations().isEmpty() || mockingDetails(task2)
+        .getInvocations().isEmpty()) {
+      weightedTasks.test(context);
+    }
+
+    verify(task1, atLeastOnce()).test(same(context));
+    verify(task2, atLeastOnce()).test(same(context));
+  }
+
+
+  @Test
+  public void callsEachTaskRoughlyRelativeToTheirWeight() throws Exception {
+    final BenchmarkDriver task1 = mock(BenchmarkDriver.class);
+    final BenchmarkDriver task2 = mock(BenchmarkDriver.class);
+
+    final Map<Object, Object> context = new ConcurrentHashMap<>();
+
+    final WeightedTasks weightedTasks =
+        new WeightedTasks(new WeightedTask(80, task1), new WeightedTask(20, task2));
+
+    final int iterations = 100000;
+    for (int i = 0; i < iterations; i++) {
+      weightedTasks.test(context);
+    }
+
+    final Method testMethod = BenchmarkDriver.class.getMethod("test", Map.class);
+
+    final double count1 = mockingDetails(task1).getInvocations().stream()
+        .filter(i -> i.getMethod().equals(testMethod)).count();
+
+    final double count2 = mockingDetails(task2).getInvocations().stream()
+        .filter(i -> i.getMethod().equals(testMethod)).count();
+
+    assertThat(count1 / iterations).isCloseTo(0.80, offset(0.01));
+    assertThat(count2 / iterations).isCloseTo(0.20, offset(0.01));
+  }
+
+}
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ClientServerBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ClientServerBenchmarkTest.java
index 0663a1d..623edfa 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ClientServerBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ClientServerBenchmarkTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.geode.benchmark.tests;
 
+import static org.apache.geode.benchmark.topology.ClientServerTopologyWithSniProxy.WITH_SNI_PROXY_PROPERTY;
 import static org.apache.geode.benchmark.topology.Ports.LOCATOR_PORT;
 import static org.apache.geode.benchmark.topology.Ports.SERVER_PORT;
 import static org.apache.geode.benchmark.topology.Ports.SNI_PROXY_PORT;
@@ -24,9 +25,10 @@ import static org.apache.geode.benchmark.topology.Roles.PROXY;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
-import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.ClearSystemProperty;
+import org.junitpioneer.jupiter.SetSystemProperty;
 
 import org.apache.geode.benchmark.tasks.StartEnvoy;
 import org.apache.geode.benchmark.tasks.StartHAProxy;
@@ -54,42 +56,37 @@ class GeodeBenchmarkTest {
             new String[] {PROXY.name()});
   }
 
-  @AfterAll
-  public static void afterAll() {
-    System.clearProperty("benchmark.withSniProxy");
-  }
-
   @Test
+  @ClearSystemProperty(key = WITH_SNI_PROXY_PROPERTY)
   public void withoutSniProxy() {
-    System.clearProperty("benchmark.withSniProxy");
     config = ClientServerBenchmark.createConfig();
     assertThat(config.getBefore()).doesNotContain(startHAProxyStep, startEnvoyStep);
   }
 
   @Test
+  @SetSystemProperty(key = WITH_SNI_PROXY_PROPERTY, value = "invalid")
   public void withSniProxyInvalid() {
-    System.setProperty("benchmark.withSniProxy", "invalid");
-    assertThatThrownBy(() -> ClientServerBenchmark.createConfig())
+    assertThatThrownBy(ClientServerBenchmark::createConfig)
         .isInstanceOf(IllegalArgumentException.class);
   }
 
   @Test
+  @SetSystemProperty(key = WITH_SNI_PROXY_PROPERTY, value = "")
   public void withSniProxyDefault() {
-    System.setProperty("benchmark.withSniProxy", "");
     config = ClientServerBenchmark.createConfig();
     assertThat(config.getBefore()).contains(startHAProxyStep).doesNotContain(startEnvoyStep);
   }
 
   @Test
+  @SetSystemProperty(key = WITH_SNI_PROXY_PROPERTY, value = "HAProxy")
   public void withSniProxyHAProxy() {
-    System.setProperty("benchmark.withSniProxy", "HAProxy");
     config = ClientServerBenchmark.createConfig();
     assertThat(config.getBefore()).contains(startHAProxyStep);
   }
 
   @Test
+  @SetSystemProperty(key = WITH_SNI_PROXY_PROPERTY, value = "Envoy")
   public void withSniProxyEnvoy() {
-    System.setProperty("benchmark.withSniProxy", "Envoy");
     config = ClientServerBenchmark.createConfig();
     assertThat(config.getBefore()).contains(startEnvoyStep);
   }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionBenchmarkTest.java
index 6b4d1df..eb6437f 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionBenchmarkTest.java
@@ -16,6 +16,7 @@ package org.apache.geode.benchmark.tests;
 
 import java.io.File;
 
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
 
 import org.apache.geode.benchmark.LongRange;
@@ -26,9 +27,11 @@ public class PartitionedFunctionExecutionBenchmarkTest {
   @TempDir
   File folder;
 
+  @Test
   public void benchmarkRunsSuccessfully() throws Exception {
     PartitionedFunctionExecutionBenchmark test = new PartitionedFunctionExecutionBenchmark();
     test.setKeyRange(new LongRange(0, 100));
+    test.setValidationEnabled(true);
     TestRunners.minimalRunner(folder).runTest(test);
   }
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionWithArgumentsBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionWithArgumentsBenchmarkTest.java
index c061986..26b06e9 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionWithArgumentsBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionWithArgumentsBenchmarkTest.java
@@ -32,6 +32,7 @@ public class PartitionedFunctionExecutionWithArgumentsBenchmarkTest {
     PartitionedFunctionExecutionWithArgumentsBenchmark test =
         new PartitionedFunctionExecutionWithArgumentsBenchmark();
     test.setKeyRange(new LongRange(0, 100));
+    test.setValidationEnabled(true);
     TestRunners.minimalRunner(folder).runTest(test);
   }
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionWithFiltersBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionWithFiltersBenchmarkTest.java
index fbfc898..10d67b5 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionWithFiltersBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedFunctionExecutionWithFiltersBenchmarkTest.java
@@ -32,7 +32,7 @@ public class PartitionedFunctionExecutionWithFiltersBenchmarkTest {
     PartitionedFunctionExecutionWithFiltersBenchmark test =
         new PartitionedFunctionExecutionWithFiltersBenchmark();
     test.setKeyRange(new LongRange(0, 100));
-    TestRunners.minimalRunner(folder)
-        .runTest(test);
+    test.setValidationEnabled(true);
+    TestRunners.minimalRunner(folder).runTest(test);
   }
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedGetBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedGetBenchmarkTest.java
index 268feee..ba3f59d 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedGetBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedGetBenchmarkTest.java
@@ -32,7 +32,7 @@ public class PartitionedGetBenchmarkTest {
       throws Exception {
     PartitionedGetBenchmark test = new PartitionedGetBenchmark();
     test.setKeyRange(new LongRange(0, 100));
-    TestRunners.minimalRunner(folder)
-        .runTest(test);
+    test.setValidationEnabled(true);
+    TestRunners.minimalRunner(folder).runTest(test);
   }
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedGetLongBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedGetLongBenchmarkTest.java
index 6430225..a4c799c 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedGetLongBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedGetLongBenchmarkTest.java
@@ -32,7 +32,7 @@ public class PartitionedGetLongBenchmarkTest {
       throws Exception {
     PartitionedGetLongBenchmark test = new PartitionedGetLongBenchmark();
     test.setKeyRange(new LongRange(0, 100));
-    TestRunners.minimalRunner(folder)
-        .runTest(test);
+    test.setValidationEnabled(true);
+    TestRunners.minimalRunner(folder).runTest(test);
   }
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedIndexedQueryBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedIndexedQueryBenchmarkTest.java
index 972eaf8..1633db8 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedIndexedQueryBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedIndexedQueryBenchmarkTest.java
@@ -31,6 +31,7 @@ public class PartitionedIndexedQueryBenchmarkTest {
     PartitionedIndexedQueryBenchmark test = new PartitionedIndexedQueryBenchmark();
     test.setKeyRange(new LongRange(0, 100));
     test.setQueryRange(10);
+    test.setValidationEnabled(true);
     TestRunners.minimalRunner(folder).runTest(test);
   }
 
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedNonIndexedQueryBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedNonIndexedQueryBenchmarkTest.java
index b9e6de7..9322817 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedNonIndexedQueryBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedNonIndexedQueryBenchmarkTest.java
@@ -31,6 +31,7 @@ public class PartitionedNonIndexedQueryBenchmarkTest {
     PartitionedNonIndexedQueryBenchmark test = new PartitionedNonIndexedQueryBenchmark();
     test.setKeyRange(new LongRange(0, 100));
     test.setQueryRange(10);
+    test.setValidationEnabled(true);
     TestRunners.minimalRunner(folder).runTest(test);
   }
 
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedPutAllBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedPutAllBenchmarkTest.java
index 15b5056..bbb6c21 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedPutAllBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedPutAllBenchmarkTest.java
@@ -35,7 +35,7 @@ public class PartitionedPutAllBenchmarkTest {
       throws Exception {
     PartitionedPutAllBenchmark test = new PartitionedPutAllBenchmark();
     test.setKeyRange(new LongRange(0, 100));
-    TestRunners.minimalRunner(folder)
-        .runTest(test);
+    test.setValidationEnabled(true);
+    TestRunners.minimalRunner(folder).runTest(test);
   }
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedPutAllLongBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedPutAllLongBenchmarkTest.java
index 5f22743..64ca18b 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedPutAllLongBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedPutAllLongBenchmarkTest.java
@@ -35,7 +35,7 @@ public class PartitionedPutAllLongBenchmarkTest {
       throws Exception {
     PartitionedPutAllLongBenchmark test = new PartitionedPutAllLongBenchmark();
     test.setKeyRange(new LongRange(0, 100));
-    TestRunners.minimalRunner(folder)
-        .runTest(test);
+    test.setValidationEnabled(true);
+    TestRunners.minimalRunner(folder).runTest(test);
   }
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedPutBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedPutBenchmarkTest.java
index 13bf6f8..007aec2 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedPutBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedPutBenchmarkTest.java
@@ -35,7 +35,7 @@ public class PartitionedPutBenchmarkTest {
       throws Exception {
     PartitionedPutBenchmark test = new PartitionedPutBenchmark();
     test.setKeyRange(new LongRange(0, 100));
-    TestRunners.minimalRunner(folder)
-        .runTest(test);
+    test.setValidationEnabled(true);
+    TestRunners.minimalRunner(folder).runTest(test);
   }
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedPutLongBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedPutLongBenchmarkTest.java
index 21454c8..0d03b60 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedPutLongBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/PartitionedPutLongBenchmarkTest.java
@@ -35,7 +35,7 @@ public class PartitionedPutLongBenchmarkTest {
       throws Exception {
     PartitionedPutLongBenchmark test = new PartitionedPutLongBenchmark();
     test.setKeyRange(new LongRange(0, 100));
-    TestRunners.minimalRunner(folder)
-        .runTest(test);
+    test.setValidationEnabled(true);
+    TestRunners.minimalRunner(folder).runTest(test);
   }
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionBenchmarkTest.java
index c7c086d..86dfc44 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionBenchmarkTest.java
@@ -31,6 +31,7 @@ public class ReplicatedFunctionExecutionBenchmarkTest {
   void benchmarkRunsSuccessfully() throws Exception {
     ReplicatedFunctionExecutionBenchmark test = new ReplicatedFunctionExecutionBenchmark();
     test.setKeyRange(new LongRange(0, 100));
+    test.setValidationEnabled(true);
     TestRunners.minimalRunner(folder).runTest(test);
   }
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionWithArgumentsBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionWithArgumentsBenchmarkTest.java
index ef91202..648e01c 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionWithArgumentsBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionWithArgumentsBenchmarkTest.java
@@ -31,6 +31,7 @@ public class ReplicatedFunctionExecutionWithArgumentsBenchmarkTest {
     ReplicatedFunctionExecutionWithArgumentsBenchmark test =
         new ReplicatedFunctionExecutionWithArgumentsBenchmark();
     test.setKeyRange(new LongRange(0, 100));
+    test.setValidationEnabled(true);
     TestRunners.minimalRunner(folder).runTest(test);
   }
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionWithFiltersBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionWithFiltersBenchmarkTest.java
index 0f9d3c0..d3b757e 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionWithFiltersBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedFunctionExecutionWithFiltersBenchmarkTest.java
@@ -31,6 +31,7 @@ public class ReplicatedFunctionExecutionWithFiltersBenchmarkTest {
     ReplicatedFunctionExecutionWithFiltersBenchmark test =
         new ReplicatedFunctionExecutionWithFiltersBenchmark();
     test.setKeyRange(new LongRange(0, 100));
+    test.setValidationEnabled(true);
     TestRunners.minimalRunner(folder).runTest(test);
   }
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedGetBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedGetBenchmarkTest.java
index 8044204..7afff8a 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedGetBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedGetBenchmarkTest.java
@@ -32,7 +32,7 @@ public class ReplicatedGetBenchmarkTest {
       throws Exception {
     ReplicatedGetBenchmark test = new ReplicatedGetBenchmark();
     test.setKeyRange(new LongRange(0, 100));
-    TestRunners.minimalRunner(folder)
-        .runTest(test);
+    test.setValidationEnabled(true);
+    TestRunners.minimalRunner(folder).runTest(test);
   }
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedGetLongBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedGetLongBenchmarkTest.java
index 423b4a8..5dcc6a3 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedGetLongBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedGetLongBenchmarkTest.java
@@ -32,7 +32,7 @@ public class ReplicatedGetLongBenchmarkTest {
       throws Exception {
     ReplicatedGetLongBenchmark test = new ReplicatedGetLongBenchmark();
     test.setKeyRange(new LongRange(0, 100));
-    TestRunners.minimalRunner(folder)
-        .runTest(test);
+    test.setValidationEnabled(true);
+    TestRunners.minimalRunner(folder).runTest(test);
   }
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedIndexedQueryBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedIndexedQueryBenchmarkTest.java
index 869b6bb..0a36ee9 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedIndexedQueryBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedIndexedQueryBenchmarkTest.java
@@ -32,6 +32,7 @@ public class ReplicatedIndexedQueryBenchmarkTest {
     ReplicatedIndexedQueryBenchmark test = new ReplicatedIndexedQueryBenchmark();
     test.setKeyRange(new LongRange(0, 100));
     test.setQueryRange(10);
+    test.setValidationEnabled(true);
     TestRunners.minimalRunner(folder).runTest(test);
   }
 
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedNonIndexedQueryBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedNonIndexedQueryBenchmarkTest.java
index c6d2c09..90436f0 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedNonIndexedQueryBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedNonIndexedQueryBenchmarkTest.java
@@ -32,6 +32,7 @@ public class ReplicatedNonIndexedQueryBenchmarkTest {
     ReplicatedNonIndexedQueryBenchmark test = new ReplicatedNonIndexedQueryBenchmark();
     test.setKeyRange(new LongRange(0, 100));
     test.setQueryRange(10);
+    test.setValidationEnabled(true);
     TestRunners.minimalRunner(folder).runTest(test);
   }
 
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedPutBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedPutBenchmarkTest.java
index 157c48c..d99d3b4 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedPutBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedPutBenchmarkTest.java
@@ -35,7 +35,7 @@ public class ReplicatedPutBenchmarkTest {
       throws Exception {
     ReplicatedPutBenchmark test = new ReplicatedPutBenchmark();
     test.setKeyRange(new LongRange(0, 100));
-    TestRunners.minimalRunner(folder)
-        .runTest(test);
+    test.setValidationEnabled(true);
+    TestRunners.minimalRunner(folder).runTest(test);
   }
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedPutLongBenchmarkTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedPutLongBenchmarkTest.java
index 8c1be3f..e78c653 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedPutLongBenchmarkTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/tests/ReplicatedPutLongBenchmarkTest.java
@@ -35,7 +35,7 @@ public class ReplicatedPutLongBenchmarkTest {
       throws Exception {
     ReplicatedPutLongBenchmark test = new ReplicatedPutLongBenchmark();
     test.setKeyRange(new LongRange(0, 100));
-    TestRunners.minimalRunner(folder)
-        .runTest(test);
+    test.setValidationEnabled(true);
+    TestRunners.minimalRunner(folder).runTest(test);
   }
 }
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/topology/ClientServerTopologyTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/topology/ClientServerTopologyTest.java
index 63a940e..8e2c050 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/topology/ClientServerTopologyTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/topology/ClientServerTopologyTest.java
@@ -16,40 +16,31 @@
 package org.apache.geode.benchmark.topology;
 
 
+import static org.apache.geode.benchmark.Constants.JAVA_RUNTIME_VERSION;
+import static org.apache.geode.benchmark.Constants.JAVA_VERSION_11;
 import static org.apache.geode.benchmark.topology.Roles.CLIENT;
+import static org.apache.geode.benchmark.topology.Topology.WITH_SECURITY_MANAGER_PROPERTY;
+import static org.apache.geode.benchmark.topology.Topology.WITH_SSL_PROPERTY;
 import static org.assertj.core.api.Assertions.assertThat;
 
-import java.util.Properties;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.ClearSystemProperty;
+import org.junitpioneer.jupiter.SetSystemProperty;
 
 import org.apache.geode.perftest.TestConfig;
 
 public class ClientServerTopologyTest {
 
-  private Properties systemProperties;
-
-  @BeforeEach
-  public void beforeEach() {
-    systemProperties = (Properties) System.getProperties().clone();
-  }
-
-  @AfterEach
-  public void afterEach() {
-    System.setProperties(systemProperties);
-  }
-
   @Test
+  @SetSystemProperty(key = WITH_SSL_PROPERTY, value = "true")
   public void configWithSsl() {
-    System.setProperty("benchmark.withSsl", "true");
     TestConfig testConfig = new TestConfig();
     ClientServerTopology.configure(testConfig);
     assertThat(testConfig.getJvmArgs().get(CLIENT.name())).contains("-Dbenchmark.withSsl=true");
   }
 
   @Test
+  @ClearSystemProperty(key = WITH_SSL_PROPERTY)
   public void configWithNoSsl() {
     TestConfig testConfig = new TestConfig();
     ClientServerTopology.configure(testConfig);
@@ -58,6 +49,7 @@ public class ClientServerTopologyTest {
   }
 
   @Test
+  @ClearSystemProperty(key = WITH_SECURITY_MANAGER_PROPERTY)
   public void configWithoutSecurityManager() {
     TestConfig testConfig = new TestConfig();
     ClientServerTopology.configure(testConfig);
@@ -66,8 +58,8 @@ public class ClientServerTopologyTest {
   }
 
   @Test
+  @SetSystemProperty(key = WITH_SECURITY_MANAGER_PROPERTY, value = "true")
   public void configWithSecurityManager() {
-    System.setProperty("benchmark.withSecurityManager", "true");
     TestConfig testConfig = new TestConfig();
     ClientServerTopology.configure(testConfig);
     assertThat(testConfig.getJvmArgs().get(CLIENT.name()))
@@ -75,10 +67,10 @@ public class ClientServerTopologyTest {
   }
 
   @Test
+  @SetSystemProperty(key = WITH_SECURITY_MANAGER_PROPERTY, value = "true")
+  @SetSystemProperty(key = WITH_SSL_PROPERTY, value = "true")
+  @SetSystemProperty(key = JAVA_RUNTIME_VERSION, value = JAVA_VERSION_11)
   public void configWithSecurityManagerAndSslAndJava11() {
-    System.setProperty("benchmark.withSecurityManager", "true");
-    System.setProperty("java.runtime.version", "11.0.4+11");
-    System.setProperty("benchmark.withSsl", "true");
     TestConfig testConfig = new TestConfig();
 
     ClientServerTopology.configure(testConfig);
diff --git a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithSniProxyTest.java b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithSniProxyTest.java
index 8938f21..edb5730 100644
--- a/geode-benchmarks/src/test/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithSniProxyTest.java
+++ b/geode-benchmarks/src/test/java/org/apache/geode/benchmark/topology/ClientServerTopologyWithSniProxyTest.java
@@ -15,14 +15,11 @@
 
 package org.apache.geode.benchmark.topology;
 
+import static java.lang.System.setProperty;
 import static org.apache.geode.benchmark.topology.ClientServerTopologyWithSniProxy.WITH_SNI_PROXY_PROPERTY;
 import static org.apache.geode.benchmark.topology.Roles.CLIENT;
 import static org.assertj.core.api.Assertions.assertThat;
 
-import java.util.Properties;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.EnumSource;
 import org.junitpioneer.jupiter.ClearSystemProperty;
@@ -32,23 +29,11 @@ import org.apache.geode.perftest.TestConfig;
 
 public class ClientServerTopologyWithSniProxyTest {
 
-  private Properties systemProperties;
-
-  @BeforeEach
-  public void beforeEach() {
-    systemProperties = (Properties) System.getProperties().clone();
-  }
-
-  @AfterEach
-  public void afterEach() {
-    System.setProperties(systemProperties);
-  }
-
   @ParameterizedTest
   @EnumSource(SniProxyImplementation.class)
   @ClearSystemProperty(key = WITH_SNI_PROXY_PROPERTY)
   public void configWithNoSsl(final SniProxyImplementation sniProxyImplementation) {
-    System.setProperty(WITH_SNI_PROXY_PROPERTY, sniProxyImplementation.name());
+    setProperty(WITH_SNI_PROXY_PROPERTY, sniProxyImplementation.name());
     final TestConfig testConfig = new TestConfig();
     ClientServerTopologyWithSniProxy.configure(testConfig);
     assertThat(testConfig.getJvmArgs().get(CLIENT.name())).contains("-Dbenchmark.withSsl=true");
diff --git a/gradle.properties b/gradle.properties
index 97c0a1a..69d970b 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -41,4 +41,4 @@ buildId = 0
 productName = Apache Geode
 productOrg = Apache Software Foundation (ASF)
 
-minimumGradleVersion = 6.6.1
\ No newline at end of file
+minimumGradleVersion = 6.8.3
diff --git a/gradle/dependency-versions.properties b/gradle/dependency-versions.properties
index a6f4102..7a5a52f 100644
--- a/gradle/dependency-versions.properties
+++ b/gradle/dependency-versions.properties
@@ -14,18 +14,19 @@
 # limitations under the License.
 
 # Dependency versions
-junit-jupiter-api.version = 5.7.0
-junit-jupiter-engine.version = 5.7.0
-junit-pioneer.version = 1.0.0
-geode-core.version = 1.13.0
-slf4j-simple.version = 1.7.25
-sshj.version = 0.26.0
-commons-io.version = 2.6
+junit-jupiter.version = 5.7.1
+junit-pioneer.version = 1.4.1
+slf4j-simple.version = 1.7.30
+sshj.version = 0.31.0
+commons-io.version = 2.8.0
 yardstick.version = 0.8.3
-HdrHistogram.version = 2.1.10
-mockito-all.version = 1.10.19
-awaitility.version = 3.0.0
-sshd-core.version = 2.1.0
-assertj-core.version = 3.11.1
-software-amazon-awssdk.version = 2.1.4
-JSON.version = 20180813
+HdrHistogram.version = 2.1.12
+mockito.version = 3.9.0
+awaitility.version = 4.1.0
+sshd-core.version = 2.6.0
+assertj-core.version = 3.19.0
+software-amazon-awssdk.version = 2.16.60
+JSON.version = 20210307
+jedis.version = 3.6.0
+lettuce.version = 6.1.1.RELEASE
+classgraph.version = 4.8.105
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ef88891..442d913 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Mon Dec 10 10:58:37 PST 2018
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip
diff --git a/harness/build.gradle b/harness/build.gradle
index ec83770..d6f2169 100644
--- a/harness/build.gradle
+++ b/harness/build.gradle
@@ -1,3 +1,5 @@
+import org.gradle.internal.jvm.Jvm
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -20,8 +22,6 @@ plugins { id 'java' }
 group 'org.apache.geode-benchmark'
 version '1.0-SNAPSHOT'
 
-sourceCompatibility = 1.8
-
 def geodeVersion = project.hasProperty('geodeVersion') ? project.findProperty('geodeVersion') : '1.+'
 def isCI = project.hasProperty('ci') ? project.findProperty('ci') : 0
 
@@ -49,14 +49,14 @@ task(analyzeRun, dependsOn: 'classes', type: JavaExec) {
 }
 
 dependencies {
-  compile(group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: project.'junit-jupiter-engine.version')
+  compile(group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: project.'junit-jupiter.version')
   compile(group: 'org.junit-pioneer', name: 'junit-pioneer', version: project.'junit-pioneer.version')
   compile(group: 'com.hierynomus', name: 'sshj', version: project.'sshj.version')
   compile(group: 'commons-io', name: 'commons-io', version: project.'commons-io.version')
   compile(group: 'org.yardstickframework', name: 'yardstick', version: project.'yardstick.version')
   compile(group: 'org.hdrhistogram', name: 'HdrHistogram', version: project.'HdrHistogram.version')
   compile(group: 'org.apache.geode', name: 'geode-core', version: geodeVersion)
-  testCompile(group: 'org.mockito', name: 'mockito-all', version: project.'mockito-all.version')
+  testCompile(group: 'org.mockito', name: 'mockito-core', version: project.'mockito.version')
   testCompile(group: 'org.awaitility', name: 'awaitility', version: project.'awaitility.version')
   testCompile(group: 'org.slf4j', name: 'slf4j-simple', version: project.'slf4j-simple.version')
   testCompile(group: 'org.apache.sshd', name: 'sshd-core', version: project.'sshd-core.version')
diff --git a/harness/src/main/java/org/apache/geode/perftest/PerformanceTest.java b/harness/src/main/java/org/apache/geode/perftest/PerformanceTest.java
index 4a97ca5..ab8ac60 100644
--- a/harness/src/main/java/org/apache/geode/perftest/PerformanceTest.java
+++ b/harness/src/main/java/org/apache/geode/perftest/PerformanceTest.java
@@ -49,4 +49,5 @@ public interface PerformanceTest extends Serializable {
    */
   TestConfig configure();
 
+
 }
diff --git a/harness/src/main/java/org/apache/geode/perftest/TestConfig.java b/harness/src/main/java/org/apache/geode/perftest/TestConfig.java
index 0e235f0..1e454e6 100644
--- a/harness/src/main/java/org/apache/geode/perftest/TestConfig.java
+++ b/harness/src/main/java/org/apache/geode/perftest/TestConfig.java
@@ -122,6 +122,10 @@ public class TestConfig implements Serializable {
     return workloadConfig.getWarmupSeconds();
   }
 
+  public int getThreads() {
+    return workloadConfig.getThreads();
+  }
+
   public Map<String, Integer> getRoles() {
     return roles;
   }
diff --git a/harness/src/main/java/org/apache/geode/perftest/TestRunners.java b/harness/src/main/java/org/apache/geode/perftest/TestRunners.java
index 27fddb3..5a71a0f 100644
--- a/harness/src/main/java/org/apache/geode/perftest/TestRunners.java
+++ b/harness/src/main/java/org/apache/geode/perftest/TestRunners.java
@@ -42,32 +42,8 @@ public class TestRunners {
   public static final String OUTPUT_DIR = "benchmark.OUTPUT_DIR";
 
   public static final String[] JVM_ARGS_SMALL_SIZE = new String[] {
-      "-XX:CMSInitiatingOccupancyFraction=60",
-      "-XX:+PrintGCDetails",
-      "-XX:+PrintGCTimeStamps",
-      "-XX:+PrintGCDateStamps",
-      "-XX:+PrintGCApplicationStoppedTime",
-      "-XX:+PrintGCApplicationConcurrentTime",
-      "-XX:+UseGCLogFileRotation",
-      "-XX:NumberOfGCLogFiles=20",
-      "-XX:GCLogFileSize=1M",
-      "-XX:+UnlockDiagnosticVMOptions",
-      "-XX:ParGCCardsPerStrideChunk=32768",
-      "-XX:+UseNUMA",
-      "-XX:+UseConcMarkSweepGC",
-      "-XX:+UseCMSInitiatingOccupancyOnly",
-      "-XX:+CMSClassUnloadingEnabled",
-      "-XX:+DisableExplicitGC",
-      "-XX:+ScavengeBeforeFullGC",
-      "-XX:+CMSScavengeBeforeRemark",
-      "-server",
-      "-Djava.awt.headless=true",
-      "-Dsun.rmi.dgc.server.gcInterval=9223372036854775806",
-      "-Dgemfire.OSProcess.ENABLE_OUTPUT_REDIRECTION=true",
-      "-Dgemfire.launcher.registerSignalHandlers=true",
       "-Xmx4g",
       "-Xms4g"
-
   };
 
   public static TestRunner defaultRunner(String username, File outputDir, String... hosts) {
diff --git a/harness/src/main/java/org/apache/geode/perftest/analysis/BenchmarkRunAnalyzer.java b/harness/src/main/java/org/apache/geode/perftest/analysis/BenchmarkRunAnalyzer.java
index 2b99cbb..fe186fa 100644
--- a/harness/src/main/java/org/apache/geode/perftest/analysis/BenchmarkRunAnalyzer.java
+++ b/harness/src/main/java/org/apache/geode/perftest/analysis/BenchmarkRunAnalyzer.java
@@ -104,7 +104,7 @@ public class BenchmarkRunAnalyzer {
 
   public static List<File> getYardstickOutputForBenchmarkDir(File benchmarkDir) throws IOException {
     return Files.walk(benchmarkDir.toPath(), 2)
-        .filter(path -> path.toString().endsWith(YardstickTask.YARDSTICK_OUTPUT))
+        .filter(path -> path.toString().contains(YardstickTask.YARDSTICK_OUTPUT))
         .map(Path::toFile)
         .collect(Collectors.toList());
   }
diff --git a/harness/src/main/java/org/apache/geode/perftest/jvms/JVMLauncher.java b/harness/src/main/java/org/apache/geode/perftest/jvms/JVMLauncher.java
index 00d52a0..4c0de66 100644
--- a/harness/src/main/java/org/apache/geode/perftest/jvms/JVMLauncher.java
+++ b/harness/src/main/java/org/apache/geode/perftest/jvms/JVMLauncher.java
@@ -25,6 +25,7 @@ import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTOR
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
 
@@ -36,6 +37,7 @@ import org.apache.geode.perftest.jvms.rmi.ChildJVM;
 
 class JVMLauncher {
   private static final Logger logger = LoggerFactory.getLogger(RemoteJVMFactory.class);
+  public static final String WITH_STRACE = "benchmark.withStrace";
 
   JVMLauncher() {}
 
@@ -53,10 +55,10 @@ class JVMLauncher {
   CompletableFuture<Void> launchWorker(Infrastructure infra, int rmiPort,
       JVMMapping jvmConfig)
       throws UnknownHostException {
-    String[] shellCommand =
-        buildCommand(InetAddress.getLocalHost().getHostAddress(), rmiPort, jvmConfig);
+    final String[] shellCommand = traceCommand(
+        buildCommand(InetAddress.getLocalHost().getHostAddress(), rmiPort, jvmConfig), jvmConfig);
 
-    CompletableFuture<Void> future = new CompletableFuture<Void>();
+    CompletableFuture<Void> future = new CompletableFuture<>();
     Thread thread = new Thread("Worker " + jvmConfig.getNode().getAddress()) {
       public void run() {
 
@@ -80,12 +82,31 @@ class JVMLauncher {
     return future;
   }
 
+  String[] traceCommand(final String[] command, JVMMapping jvmConfig) {
+    List<String> strace = new ArrayList<>();
+
+    if (Boolean.getBoolean(WITH_STRACE)) {
+      strace.add("strace");
+      strace.add("-o");
+      strace.add(jvmConfig.getOutputDir() + "/java.strace");
+      strace.add("-ttt");
+      strace.add("-T");
+      strace.add("-f");
+      strace.add("-ff");
+    }
+
+    strace.addAll(Arrays.asList(command));
+
+    return strace.toArray(new String[0]);
+  }
+
   String[] buildCommand(String rmiHost, int rmiPort, JVMMapping jvmConfig) {
 
-    List<String> command = new ArrayList<String>();
+    List<String> command = new ArrayList<>();
     command.add(System.getProperty("java.home") + "/bin/java");
     command.add("-classpath");
     command.add(jvmConfig.getLibDir() + "/*");
+    command.add("-Djava.library.path=" + System.getProperty("user.home") + "/META-INF/native");
     command.add("-D" + RemoteJVMFactory.RMI_HOST + "=" + rmiHost);
     command.add("-D" + RemoteJVMFactory.RMI_PORT_PROPERTY + "=" + rmiPort);
     command.add("-D" + RemoteJVMFactory.JVM_ID + "=" + jvmConfig.getId());
diff --git a/harness/src/main/java/org/apache/geode/perftest/jvms/RemoteJVMFactory.java b/harness/src/main/java/org/apache/geode/perftest/jvms/RemoteJVMFactory.java
index 665d726..10086c4 100644
--- a/harness/src/main/java/org/apache/geode/perftest/jvms/RemoteJVMFactory.java
+++ b/harness/src/main/java/org/apache/geode/perftest/jvms/RemoteJVMFactory.java
@@ -129,7 +129,7 @@ public class RemoteJVMFactory {
     CompletableFuture<Void> processesExited = jvmLauncher.launchProcesses(infra, RMI_PORT, mapping);
 
     if (!controller.waitForWorkers(5, TimeUnit.MINUTES)) {
-      throw new IllegalStateException("Workers failed to start in 1 minute");
+      throw new IllegalStateException("Workers failed to start in 5 minute");
     }
 
     return new RemoteJVMs(infra, mapping, controller, processesExited);
diff --git a/harness/src/main/java/org/apache/geode/perftest/runner/DefaultTestRunner.java b/harness/src/main/java/org/apache/geode/perftest/runner/DefaultTestRunner.java
index fcac72b..d7d619d 100644
--- a/harness/src/main/java/org/apache/geode/perftest/runner/DefaultTestRunner.java
+++ b/harness/src/main/java/org/apache/geode/perftest/runner/DefaultTestRunner.java
@@ -139,6 +139,7 @@ public class DefaultTestRunner implements TestRunner {
   private void runTasks(List<TestStep> steps,
       RemoteJVMs remoteJVMs) {
     steps.forEach(testStep -> {
+      logger.info("Executing task {} on {}...", testStep.getTask(), testStep.getRoles());
       remoteJVMs.execute(testStep.getTask(), testStep.getRoles());
     });
   }
diff --git a/harness/src/test/java/org/apache/geode/perftest/BenchmarkPropertiesTest.java b/harness/src/test/java/org/apache/geode/perftest/BenchmarkPropertiesTest.java
index 0d6ceef..379f330 100644
--- a/harness/src/test/java/org/apache/geode/perftest/BenchmarkPropertiesTest.java
+++ b/harness/src/test/java/org/apache/geode/perftest/BenchmarkPropertiesTest.java
@@ -22,14 +22,14 @@ import java.util.Map;
 
 import org.assertj.core.api.Assertions;
 import org.junit.jupiter.api.Test;
+import org.junitpioneer.jupiter.SetSystemProperty;
 
 class BenchmarkPropertiesTest {
 
   @Test
+  @SetSystemProperty(key = "benchmark.system.role1.p1", value = "v1")
+  @SetSystemProperty(key = "benchmark.system.role1.p2", value = "v2")
   public void canParsePropertiesForRoles() {
-    System.setProperty("benchmark.system.role1.p1", "v1");
-    System.setProperty("benchmark.system.role1.p2", "v2");
-
     Map<String, List<String>> defaultArgs =
         BenchmarkProperties.getDefaultJVMArgs();
 
diff --git a/harness/src/test/java/org/apache/geode/perftest/TestRunnerIntegrationTest.java b/harness/src/test/java/org/apache/geode/perftest/TestRunnerIntegrationTest.java
index 2bddb1a..a3d9f88 100644
--- a/harness/src/test/java/org/apache/geode/perftest/TestRunnerIntegrationTest.java
+++ b/harness/src/test/java/org/apache/geode/perftest/TestRunnerIntegrationTest.java
@@ -31,6 +31,7 @@ import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
+import org.junitpioneer.jupiter.SetSystemProperty;
 
 import org.apache.geode.perftest.benchmarks.EmptyBenchmark;
 import org.apache.geode.perftest.infrastructure.local.LocalInfrastructureFactory;
@@ -46,8 +47,6 @@ public class TestRunnerIntegrationTest {
   @TempDir
   File outputDir;
 
-  public static final String SAMPLE_BENCHMARK = "SampleBenchmark";
-
   @BeforeEach
   void beforeEach() {
     runner = new DefaultTestRunner(new RemoteJVMFactory(new LocalInfrastructureFactory()),
@@ -100,8 +99,8 @@ public class TestRunnerIntegrationTest {
   }
 
   @Test
+  @SetSystemProperty(key = TEST_PROPERTY, value = "p3")
   public void configuresJVMOptions() throws Exception {
-    System.setProperty(TEST_PROPERTY, "p3");
     runner.runTest(() -> {
       TestConfig testConfig = new TestConfig();
       testConfig.role("all", 1);
diff --git a/harness/src/test/java/org/apache/geode/perftest/infrastructure/ssh/SshInfrastructureTest.java b/harness/src/test/java/org/apache/geode/perftest/infrastructure/ssh/SshInfrastructureTest.java
index c1948ff..4acd16a 100644
--- a/harness/src/test/java/org/apache/geode/perftest/infrastructure/ssh/SshInfrastructureTest.java
+++ b/harness/src/test/java/org/apache/geode/perftest/infrastructure/ssh/SshInfrastructureTest.java
@@ -30,6 +30,7 @@ import java.util.Set;
 import java.util.concurrent.ExecutionException;
 
 import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.channel.ChannelSession;
 import org.apache.sshd.server.command.Command;
 import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
 import org.apache.sshd.server.shell.ProcessShellCommandFactory;
@@ -78,8 +79,9 @@ public class SshInfrastructureTest {
 
   private class UnescapingCommandFactory extends ProcessShellCommandFactory {
     @Override
-    public Command createCommand(String command) {
-      return super.createCommand(command.replace("'", ""));
+    public Command createCommand(final ChannelSession channel, final String command)
+        throws IOException {
+      return super.createCommand(channel, command.replace("'", ""));
     }
   }
 
diff --git a/harness/src/test/java/org/apache/geode/perftest/jvms/RemoteJVMFactoryTest.java b/harness/src/test/java/org/apache/geode/perftest/jvms/RemoteJVMFactoryTest.java
index c1a106e..0ecb192 100644
--- a/harness/src/test/java/org/apache/geode/perftest/jvms/RemoteJVMFactoryTest.java
+++ b/harness/src/test/java/org/apache/geode/perftest/jvms/RemoteJVMFactoryTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.geode.perftest.jvms;
 
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
@@ -72,7 +73,7 @@ public class RemoteJVMFactoryTest {
     Set<Infrastructure.Node> nodes = Stream.of(node1, node2).collect(Collectors.toSet());
     when(infra.getNodes()).thenReturn(nodes);
 
-    when(controller.waitForWorkers(anyInt(), any())).thenReturn(true);
+    when(controller.waitForWorkers(anyLong(), any())).thenReturn(true);
 
     factory.launch(roles, Collections.emptyMap());
 
@@ -80,7 +81,7 @@ public class RemoteJVMFactoryTest {
 
     inOrder.verify(controllerFactory).createController(any(), eq(2));
     inOrder.verify(jvmLauncher).launchProcesses(eq(infra), anyInt(), any());
-    inOrder.verify(controller).waitForWorkers(anyInt(), any());
+    inOrder.verify(controller).waitForWorkers(anyLong(), any());
 
 
 
diff --git a/infrastructure/build.gradle b/infrastructure/build.gradle
index a9b9a8b..4c78d01 100644
--- a/infrastructure/build.gradle
+++ b/infrastructure/build.gradle
@@ -48,6 +48,9 @@ task(launchCluster, dependsOn: 'classes', type: JavaExec) {
 
   systemProperty 'TEST_CI', project.findProperty('ci')
   systemProperty 'PURPOSE', project.findProperty('purpose')
+  systemProperty 'INSTANCE_TYPE', project.findProperty('instanceType')
+  systemProperty 'TENANCY', project.findProperty('tenancy')
+  systemProperty 'AVAILABILITY_ZONE', project.findProperty('availabilityZone')
 }
 
 task(destroyCluster, dependsOn: 'classes', type: JavaExec) {
diff --git a/infrastructure/scripts/aws/README.md b/infrastructure/scripts/aws/README.md
index 177de15..b5d19ce 100644
--- a/infrastructure/scripts/aws/README.md
+++ b/infrastructure/scripts/aws/README.md
@@ -46,14 +46,17 @@ Creates an instance group in AWS based on an image created.
 
 Usage:
 
-    ./launch_cluster.sh -t [tag] -c [count] [--ci]
+    ./launch_cluster.sh -t [tag] -c [count]
 
 Options:
 
-    -t|--tag     : Cluster tag to identify the cluster for use with other utilities
-    -c|--count   : Number of AWS instances to start (recommended: 4)
-    --ci         : (Optional) Set when the instances are being started for use in Continuous Integration
-    -h|-?|--help : Help message
+    -t|--tag             : Cluster tag to identify the cluster for use with other utilities
+    -c|--count           : Number of AWS instances to start (recommended: 4)
+    -i|--instance-type   : AWS instance type to start (default: c5.18xlarge)
+    --tenancy            : AWS tenancy. (default: host)
+    --availability-zone  : AWS availability zone. (default: us-west-2a)
+    --ci                 : (Optional) Set when the instances are being started for use in Continuous Integration
+    -h|-?|--help         : Help message
 
 # run_tests.sh
 Runs benchmark tests against a single branch of `geode` on the AWS instances with the specified tag.
diff --git a/infrastructure/scripts/aws/copy_to_cluster.sh b/infrastructure/scripts/aws/copy_to_cluster.sh
index 9b9a95c..b5b6d02 100755
--- a/infrastructure/scripts/aws/copy_to_cluster.sh
+++ b/infrastructure/scripts/aws/copy_to_cluster.sh
@@ -60,10 +60,12 @@ if [[ -z "${AWS_ACCESS_KEY_ID}" ]]; then
   export AWS_PROFILE="geode-benchmarks"
 fi
 
-SSH_OPTIONS="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i ~/.geode-benchmarks/${TAG}-privkey.pem"
+SSH_OPTIONS="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=ERROR -i ~/.geode-benchmarks/${TAG}-privkey.pem"
 HOSTS=`aws ec2 describe-instances --query 'Reservations[*].Instances[*].PublicIpAddress' --filter "Name=tag:geode-benchmarks,Values=${TAG}" --output text`
 
 for host in ${HOSTS}; do
   echo "Copying to ${host}."
-  scp ${SSH_OPTIONS} ${@: 1:$#-1} geode@${host}:${@: -1}
+  scp ${SSH_OPTIONS} ${@: 1:$#-1} geode@${host}:${@: -1} &
 done
+
+wait
diff --git a/infrastructure/scripts/aws/launch_cluster.sh b/infrastructure/scripts/aws/launch_cluster.sh
index 600f0d9..563e728 100755
--- a/infrastructure/scripts/aws/launch_cluster.sh
+++ b/infrastructure/scripts/aws/launch_cluster.sh
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+    #!/usr/bin/env bash
 
 #
 # Licensed to the Apache Software Foundation (ASF) under one
@@ -43,6 +43,33 @@ while (( "$#" )); do
         exit 1
       fi
       ;;
+    -i|--instance-type )
+      if [ "$2" ]; then
+        INSTANCE_TYPE=$2
+        shift
+      else
+        echo 'ERROR: "--instance-type" requires a non-empty argument.'
+        exit 1
+      fi
+      ;;
+    --tenancy )
+      if [ "$2" ]; then
+        TENANCY=$2
+        shift
+      else
+        echo 'ERROR: "--tenancy" requires a non-empty argument.'
+        exit 1
+      fi
+      ;;
+    --availability-zone )
+      if [ "$2" ]; then
+        AVAILABILITY_ZONE=$2
+        shift
+      else
+        echo 'ERROR: "--availability-zone" requires a non-empty argument.'
+        exit 1
+      fi
+      ;;
     --ci )
       CI=1
       ;;
@@ -60,6 +87,9 @@ while (( "$#" )); do
       echo "Options:"
       echo "-t|--tag : Cluster tag"
       echo "-c|--count : The number of instances to start"
+      echo "-i|--instance-type : The instance type to start"
+      echo "--tenancy : Optionally 'host' or 'dedicated' (default)"
+      echo "--availability-zone : Optionally AWS AZ, default 'us-west-2a'"
       echo "--ci : Set if starting instances for Continuous Integration"
       echo "-p|--purpose : Purpose (Purpose tag to use for base AMI)"
       echo "-- : All subsequent arguments are passed to the benchmark task as arguments."
@@ -82,9 +112,16 @@ if [[ -z "${AWS_ACCESS_KEY_ID}" ]]; then
   export AWS_PROFILE="geode-benchmarks"
 fi
 
+INSTANCE_TYPE=${INSTANCE_TYPE:-"c5.18xlarge"}
+AVAILABILITY_ZONE=${AVAILABILITY_ZONE:-"us-west-2a"}
+TENANCY=${TENANCY:-"host"}
 CI=${CI:-0}
 PURPOSE=${PURPOSE:-"geode-benchmarks"}
 
 pushd ../../../
-./gradlew launchCluster -Pci=${CI} -Ppurpose=${PURPOSE} --args "${TAG} ${COUNT}"
+./gradlew launchCluster -Pci=${CI} -Ppurpose=${PURPOSE} \
+      -PinstanceType=${INSTANCE_TYPE} \
+      -Ptenancy=${TENANCY} \
+      -PavailabilityZone=${AVAILABILITY_ZONE} \
+      --args "${TAG} ${COUNT}"
 popd
diff --git a/infrastructure/scripts/aws/run_on_cluster.sh b/infrastructure/scripts/aws/run_on_cluster.sh
index b959221..b168418 100755
--- a/infrastructure/scripts/aws/run_on_cluster.sh
+++ b/infrastructure/scripts/aws/run_on_cluster.sh
@@ -60,10 +60,12 @@ if [[ -z "${AWS_ACCESS_KEY_ID}" ]]; then
   export AWS_PROFILE="geode-benchmarks"
 fi
 
-SSH_OPTIONS="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i ~/.geode-benchmarks/${TAG}-privkey.pem"
+SSH_OPTIONS="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=ERROR -i ~/.geode-benchmarks/${TAG}-privkey.pem"
 HOSTS=`aws ec2 describe-instances --query 'Reservations[*].Instances[*].PublicIpAddress' --filter "Name=tag:geode-benchmarks,Values=${TAG}" --output text`
 
 for host in ${HOSTS}; do
   echo "Executing on ${host}."
-  ssh ${SSH_OPTIONS} geode@${host} "$@"
+  ssh ${SSH_OPTIONS} geode@${host} "$@" &
 done
+
+wait
diff --git a/infrastructure/scripts/aws/run_tests.sh b/infrastructure/scripts/aws/run_tests.sh
index 2278b35..7eaf49d 100755
--- a/infrastructure/scripts/aws/run_tests.sh
+++ b/infrastructure/scripts/aws/run_tests.sh
@@ -167,9 +167,10 @@ if [[ -z "${VERSION}" ]]; then
     rm -f "${FILES}"
     popd
   else
-    remoteShell rm -rf geode '&&' git clone "${REPO}" geode
+    remoteShell "cd geode && [ \"${REPO}\" == \"\$(git remote get-url origin)\" ] && git fetch origin" \
+      || remoteShell rm -rf geode '&&' git clone "${REPO}" geode
   fi
-  remoteShell cd geode '&&' git checkout "${BRANCH}"
+  remoteShell "cd geode && git checkout \"${BRANCH}\" && [ \"\$(git rev-parse --abbrev-ref --symbolic-full-name HEAD)\" == \"HEAD\" ] || git reset --hard \"origin/${BRANCH}\""
 
   set +e
   for i in {1..5}; do
@@ -202,11 +203,10 @@ set -e
 
 instance_id=$(remoteShell cat .geode-benchmarks-identifier)
 
+remoteShell "cd geode-benchmarks && [ \"${BENCHMARK_REPO}\" == \"\$(git remote get-url origin)\" ] && git fetch origin" \
+  || remoteShell rm -rf geode-benchmarks '&&' git clone "${BENCHMARK_REPO}"
 
-remoteShell \
-  rm -rf geode-benchmarks '&&' \
-  git clone "${BENCHMARK_REPO}" '&&' \
-  cd geode-benchmarks '&&' git checkout "${BENCHMARK_BRANCH}"
+remoteShell cd geode-benchmarks '&&' git checkout "${BENCHMARK_BRANCH}" '&&' git reset --hard "origin/${BENCHMARK_BRANCH}"
 
 BENCHMARK_SHA=$(remoteShell \
   cd geode-benchmarks '&&' \
@@ -216,6 +216,8 @@ BUILD_IDENTIFIER="$(uuidgen)"
 
 METADATA="${METADATA},'source_repo':'${GEODE_REPO}','benchmark_repo':'${BENCHMARK_REPO}','benchmark_branch':'${BENCHMARK_BRANCH}','instance_id':'${instance_id}','benchmark_sha':'${BENCHMARK_SHA}','build_identifier':'${BUILD_IDENTIFIER}'"
 
+remoteShell rm -rf 'geode-benchmarks/geode-benchmarks/build/reports' 'geode-benchmarks/geode-benchmarks/build/benchmarks_*'
+
 remoteShell \
   cd geode-benchmarks '&&' \
   ./gradlew -PgeodeVersion="${VERSION}" benchmark "-Phosts=${HOSTS}" "-Pmetadata=${METADATA}" "$@"
diff --git a/infrastructure/src/main/java/org/apache/geode/infrastructure/aws/AwsBenchmarkMetadata.java b/infrastructure/src/main/java/org/apache/geode/infrastructure/aws/AwsBenchmarkMetadata.java
index ed99626..8d0dec2 100644
--- a/infrastructure/src/main/java/org/apache/geode/infrastructure/aws/AwsBenchmarkMetadata.java
+++ b/infrastructure/src/main/java/org/apache/geode/infrastructure/aws/AwsBenchmarkMetadata.java
@@ -16,6 +16,9 @@
  */
 package org.apache.geode.infrastructure.aws;
 
+import static java.lang.System.getProperty;
+import static software.amazon.awssdk.services.ec2.model.InstanceType.C5_18_XLARGE;
+
 import software.amazon.awssdk.services.ec2.model.InstanceType;
 import software.amazon.awssdk.services.ec2.model.PlacementStrategy;
 import software.amazon.awssdk.services.ec2.model.Tenancy;
@@ -28,7 +31,8 @@ import org.apache.geode.infrastructure.BenchmarkMetadata;
 class AwsBenchmarkMetadata extends BenchmarkMetadata {
   public static final String USER = "geode";
   public static final int POLL_INTERVAL = 15000;
-  public static InstanceType INSTANCE_TYPE = InstanceType.C5_18_XLARGE;
+  public static InstanceType INSTANCE_TYPE = InstanceType.fromValue(
+      getProperty("INSTANCE_TYPE", C5_18_XLARGE.toString()));
   public static Tenancy TENANCY = Tenancy.DEDICATED;
 
   public static String securityGroup(String tag) {
diff --git a/infrastructure/src/main/java/org/apache/geode/infrastructure/aws/LaunchCluster.java b/infrastructure/src/main/java/org/apache/geode/infrastructure/aws/LaunchCluster.java
index 1ba4cfe..e1cc366 100644
--- a/infrastructure/src/main/java/org/apache/geode/infrastructure/aws/LaunchCluster.java
+++ b/infrastructure/src/main/java/org/apache/geode/infrastructure/aws/LaunchCluster.java
@@ -16,6 +16,7 @@
  */
 package org.apache.geode.infrastructure.aws;
 
+import static java.lang.String.format;
 import static java.lang.Thread.sleep;
 
 import java.io.FileWriter;
@@ -78,7 +79,12 @@ public class LaunchCluster {
   private static final int MAX_DESCRIBE_RETRIES = 5;
   private static final int MAX_CREATE_RETRIES = 3;
   private static final int MAX_TAG_RETRIES = 3;
-  static Ec2Client ec2 = Ec2Client.create();
+
+  private static final Ec2Client ec2 = Ec2Client.create();
+
+  private static final Tenancy tenancy = Tenancy.fromValue(System.getProperty("TENANCY", "host"));
+  private static final String availabilityZone =
+      System.getProperty("AVAILABILITY_ZONE", "us-west-2a");
 
   public static void main(String[] args) throws IOException, InterruptedException {
     if (args.length != 2) {
@@ -92,20 +98,21 @@ public class LaunchCluster {
       usage("Usage: LaunchCluster <tag> <count>");
     }
 
-    List<Tag> tags = getTags(benchmarkTag);
+    final List<Tag> tags = getTags(benchmarkTag);
     createKeyPair(benchmarkTag);
-    Image newestImage = getNewestImage();
+
+    final Image newestImage = getNewestImage();
 
     createPlacementGroup(benchmarkTag);
     createSecurityGroup(benchmarkTag, tags);
     authorizeSecurityGroup(benchmarkTag);
     createLaunchTemplate(benchmarkTag, newestImage);
 
-    int ec2Timeout = 300;
-    List<String> hostIds = allocateHosts(tags, count, ec2Timeout);
-    List<String> instanceIds = launchInstances(benchmarkTag, tags, hostIds);
-    DescribeInstancesResponse instances = waitForInstances(instanceIds, ec2Timeout);
-    List<String> publicIps = getPublicIps(instances);
+    final int ec2Timeout = 300;
+    final List<String> instanceIds = launchInstances(benchmarkTag, count, tags, ec2Timeout);
+    final DescribeInstancesResponse instances = waitForInstances(instanceIds, ec2Timeout);
+    final List<String> publicIps = getPublicIps(instances);
+
     createMetadata(benchmarkTag, publicIps);
     installPrivateKey(benchmarkTag, publicIps);
     installMetadata(benchmarkTag, publicIps);
@@ -113,6 +120,21 @@ public class LaunchCluster {
     System.out.println("Instances successfully launched! Public IPs: " + publicIps);
   }
 
+  private static List<String> launchInstances(final String benchmarkTag, final int count,
+      final List<Tag> tags, final int ec2Timeout)
+      throws InterruptedException {
+    switch (tenancy) {
+      case HOST:
+        final List<String> hostIds = allocateHosts(tags, count, ec2Timeout);
+        return launchInstances(benchmarkTag, tags, hostIds);
+      case DEDICATED:
+      case DEFAULT:
+        return launchInstances(benchmarkTag, tags, count);
+      default:
+        throw new IllegalArgumentException(format("Unsupported tenancy mode: %s", tenancy));
+    }
+  }
+
   private static List<String> getPublicIps(DescribeInstancesResponse describeInstancesResponse) {
     return describeInstancesResponse.reservations().stream()
         .flatMap(reservation -> reservation.instances().stream())
@@ -132,7 +154,7 @@ public class LaunchCluster {
     Instant end = Instant.now().plus(Duration.ofSeconds(timeout));
     do {
       hosts = ec2.allocateHosts(AllocateHostsRequest.builder()
-          .availabilityZone("us-west-2a")
+          .availabilityZone(availabilityZone)
           .instanceType(AwsBenchmarkMetadata.instanceType().toString())
           .quantity(count - gotHosts)
           .tagSpecifications(TagSpecification.builder()
@@ -151,9 +173,9 @@ public class LaunchCluster {
     return hostIds;
   }
 
-  private static List<String> launchInstances(String launchTemplate, List<Tag> tags,
-      List<String> hosts) {
-    List<String> instanceIds = new ArrayList<>(hosts.size());
+  private static List<String> launchInstances(final String launchTemplate, final List<Tag> tags,
+      final List<String> hosts) {
+    final List<String> instanceIds = new ArrayList<>(hosts.size());
     for (String host : hosts) {
       RunInstancesResponse rir = ec2.runInstances(RunInstancesRequest.builder()
           .launchTemplate(LaunchTemplateSpecification.builder()
@@ -177,6 +199,26 @@ public class LaunchCluster {
     return instanceIds;
   }
 
+  private static List<String> launchInstances(final String launchTemplate, final List<Tag> tags,
+      final int count) {
+    RunInstancesResponse rir = ec2.runInstances(RunInstancesRequest.builder()
+        .launchTemplate(LaunchTemplateSpecification.builder()
+            .launchTemplateName(AwsBenchmarkMetadata.launchTemplate(launchTemplate))
+            .build())
+        .placement(Placement.builder()
+            .availabilityZone(availabilityZone)
+            .tenancy(tenancy)
+            .build())
+        .tagSpecifications(TagSpecification.builder()
+            .tags(tags)
+            .resourceType(ResourceType.INSTANCE)
+            .build())
+        .minCount(count)
+        .maxCount(count)
+        .build());
+    return rir.instances().stream().map(Instance::instanceId).collect(Collectors.toList());
+  }
+
   private static DescribeInstancesResponse waitForInstances(List<String> instanceIds, int timeout)
       throws InterruptedException {
     System.out.println("Waiting for cluster instances to go fully online.");