diff --git a/common/perf-test-core/src/main/java/com/azure/perf/test/core/PerfStressOptions.java b/common/perf-test-core/src/main/java/com/azure/perf/test/core/PerfStressOptions.java index 2c0bc50d9307..7bc70c3e74eb 100644 --- a/common/perf-test-core/src/main/java/com/azure/perf/test/core/PerfStressOptions.java +++ b/common/perf-test-core/src/main/java/com/azure/perf/test/core/PerfStressOptions.java @@ -4,8 +4,11 @@ package com.azure.perf.test.core; import java.net.URI; +import java.util.Arrays; +import java.util.List; import com.beust.jcommander.Parameter; +import com.beust.jcommander.converters.IParameterSplitter; import com.fasterxml.jackson.annotation.JsonPropertyOrder; /** @@ -19,8 +22,8 @@ public class PerfStressOptions { @Parameter(names = { "--insecure" }, description = "Allow untrusted SSL server certs") private boolean insecure = false; - @Parameter(names = { "-x", "--test-proxy" }, description = "URI of TestProxy Server") - private URI testProxy; + @Parameter(names = { "-x", "--test-proxies" }, splitter = SemiColonSplitter.class, description = "URIs of TestProxy Servers (separated by ';')") + private List testProxies; @Parameter(names = { "-i", "--iterations" }, description = "Number of iterations of main test loop") private int iterations = 1; @@ -79,8 +82,8 @@ public boolean isInsecure() { * Get the configured test proxy for performance test. * @return The configured test proxy. */ - public URI getTestProxy() { - return testProxy; + public List getTestProxies() { + return testProxies; } /** @@ -122,4 +125,10 @@ public int getWarmup() { public boolean isSync() { return sync; } + + private static class SemiColonSplitter implements IParameterSplitter { + public List split(String value) { + return Arrays.asList(value.split(";")); + } + } } diff --git a/common/perf-test-core/src/main/java/com/azure/perf/test/core/PerfStressProgram.java b/common/perf-test-core/src/main/java/com/azure/perf/test/core/PerfStressProgram.java index 1ffd7f6f134a..40088937ca68 100644 --- a/common/perf-test-core/src/main/java/com/azure/perf/test/core/PerfStressProgram.java +++ b/common/perf-test-core/src/main/java/com/azure/perf/test/core/PerfStressProgram.java @@ -141,7 +141,7 @@ public static void run(Class testClass, PerfStressOptions options) { Flux.just(tests).flatMap(PerfStressTest::setupAsync).blockLast(); setupStatus.dispose(); - if (options.getTestProxy() != null) { + if (options.getTestProxies() != null && !options.getTestProxies().isEmpty()) { Disposable recordStatus = printStatus("=== Record and Start Playback ===", () -> ".", false, false); Flux.just(tests).flatMap(PerfStressTest::recordAndStartPlaybackAsync).blockLast(); startedPlayback = true; diff --git a/common/perf-test-core/src/main/java/com/azure/perf/test/core/PerfStressTest.java b/common/perf-test-core/src/main/java/com/azure/perf/test/core/PerfStressTest.java index 2cec027f8b58..28902219e68e 100644 --- a/common/perf-test-core/src/main/java/com/azure/perf/test/core/PerfStressTest.java +++ b/common/perf-test-core/src/main/java/com/azure/perf/test/core/PerfStressTest.java @@ -12,7 +12,9 @@ import reactor.core.publisher.Mono; import java.lang.reflect.Method; +import java.net.URI; import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; import javax.net.ssl.SSLException; /** @@ -28,6 +30,7 @@ */ public abstract class PerfStressTest { private final reactor.netty.http.client.HttpClient recordPlaybackHttpClient; + private final URI testProxy; private final TestProxyPolicy testProxyPolicy; private String recordingId; @@ -38,6 +41,9 @@ public abstract class PerfStressTest { protected final HttpClient httpClient; protected final Iterable policies; + private static final AtomicInteger globalParallelIndex = new AtomicInteger(); + protected final int parallelIndex; + /** * Creates an instance of performance test. * @param options the options configured for the test. @@ -45,6 +51,7 @@ public abstract class PerfStressTest { */ public PerfStressTest(TOptions options) { this.options = options; + this.parallelIndex = globalParallelIndex.getAndIncrement(); final SslContext sslContext; @@ -66,7 +73,7 @@ public PerfStressTest(TOptions options) { httpClient = null; } - if (options.getTestProxy() != null) { + if (options.getTestProxies() != null && !options.getTestProxies().isEmpty()) { if (options.isInsecure()) { recordPlaybackHttpClient = reactor.netty.http.client.HttpClient.create() .secure(sslContextSpec -> sslContextSpec.sslContext(sslContext)); @@ -74,10 +81,12 @@ public PerfStressTest(TOptions options) { recordPlaybackHttpClient = reactor.netty.http.client.HttpClient.create(); } - testProxyPolicy = new TestProxyPolicy(options.getTestProxy()); + testProxy = options.getTestProxies().get(parallelIndex % options.getTestProxies().size()); + testProxyPolicy = new TestProxyPolicy(testProxy); policies = Arrays.asList(testProxyPolicy); } else { recordPlaybackHttpClient = null; + testProxy = null; testProxyPolicy = null; policies = null; } @@ -179,7 +188,7 @@ public Mono stopPlaybackAsync() { h.set("x-purge-inmemory-recording", Boolean.toString(true)); }) .post() - .uri(options.getTestProxy().resolve("/playback/stop")) + .uri(testProxy.resolve("/playback/stop")) .response() .doOnSuccess(response -> { testProxyPolicy.setMode(null); @@ -207,7 +216,7 @@ public Mono globalCleanupAsync() { private Mono startRecordingAsync() { return recordPlaybackHttpClient .post() - .uri(options.getTestProxy().resolve("/record/start")) + .uri(testProxy.resolve("/record/start")) .response() .doOnNext(response -> { recordingId = response.responseHeaders().get("x-recording-id"); @@ -219,7 +228,7 @@ private Mono stopRecordingAsync() { return recordPlaybackHttpClient .headers(h -> h.set("x-recording-id", recordingId)) .post() - .uri(options.getTestProxy().resolve("/record/stop")) + .uri(testProxy.resolve("/record/stop")) .response() .then(); } @@ -228,7 +237,7 @@ private Mono startPlaybackAsync() { return recordPlaybackHttpClient .headers(h -> h.set("x-recording-id", recordingId)) .post() - .uri(options.getTestProxy().resolve("/playback/start")) + .uri(testProxy.resolve("/playback/start")) .response() .doOnNext(response -> { recordingId = response.responseHeaders().get("x-recording-id");