From 5d152cfe8ec762ef701d523668bcc14c676ae4f4 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Wed, 6 Oct 2021 18:36:29 -0700 Subject: [PATCH 1/3] [Perf] Call Run() once before starting recording - Avoids capturing one-time setup like authorization requests --- .../azure/perf/test/core/HttpPipelineOptions.java | 12 ++++++++++++ .../com/azure/perf/test/core/HttpPipelineTest.java | 11 +++++++++++ .../com/azure/perf/test/core/PerfStressTest.java | 4 +++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/common/perf-test-core/src/main/java/com/azure/perf/test/core/HttpPipelineOptions.java b/common/perf-test-core/src/main/java/com/azure/perf/test/core/HttpPipelineOptions.java index 53d1046ad4ee..6ba9b238f8dc 100644 --- a/common/perf-test-core/src/main/java/com/azure/perf/test/core/HttpPipelineOptions.java +++ b/common/perf-test-core/src/main/java/com/azure/perf/test/core/HttpPipelineOptions.java @@ -14,6 +14,10 @@ public class HttpPipelineOptions extends PerfStressOptions { @Parameter(names = { "-u", "--url" }, description = "URL to fetch", required = true) private URL url; + @Parameter(names = { "--first-run-extra-requests" }, description = "Extra requests to send on first run. " + + "Simulates SDKs which require extra requests (like authentication) on first API call.") + private int firstRunExtraRequests = 0; + /** * Returns the URL used by the HTTP request. * @return The URL used by the HTTP request. @@ -21,4 +25,12 @@ public class HttpPipelineOptions extends PerfStressOptions { public URL getUrl() { return url; } + + /** + * Returns the extra requests to send on first run. + * @return The extra requests to send on first run. + */ + public int getFirstRunExtraRequests() { + return firstRunExtraRequests; + } } diff --git a/common/perf-test-core/src/main/java/com/azure/perf/test/core/HttpPipelineTest.java b/common/perf-test-core/src/main/java/com/azure/perf/test/core/HttpPipelineTest.java index 9ca500998ac2..c27310b5ede1 100644 --- a/common/perf-test-core/src/main/java/com/azure/perf/test/core/HttpPipelineTest.java +++ b/common/perf-test-core/src/main/java/com/azure/perf/test/core/HttpPipelineTest.java @@ -21,6 +21,7 @@ public class HttpPipelineTest extends PerfStressTest { private final HttpPipeline httpPipeline; private final byte[] buffer = new byte[BUFFER_SIZE]; + private boolean _firstRun = true; /** * Creates an instance of the {@link HttpPipelineTest}. @@ -53,6 +54,16 @@ public void run() { @Override public Mono runAsync() { + if (_firstRun) { + _firstRun = false; + return sendRequest().repeat(options.getFirstRunExtraRequests()).then(); + } + else { + return sendRequest(); + } + } + + public Mono sendRequest() { HttpRequest request = new HttpRequest(HttpMethod.GET, options.getUrl()); return httpPipeline .send(request) 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 7c201c015c67..2cec027f8b58 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 @@ -132,7 +132,9 @@ public Mono setupAsync() { * @return An empty {@link Mono}. */ public Mono recordAndStartPlaybackAsync() { - return startRecordingAsync() + // Make one call to Run() before starting recording, to avoid capturing one-time setup like authorization requests. + return runSyncOrAsync() + .then(startRecordingAsync()) .doOnSuccess(x -> { testProxyPolicy.setRecordingId(recordingId); testProxyPolicy.setMode("record"); From f83027b6ce8b693482dd5fc4c7c1df02567689a1 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Thu, 7 Oct 2021 11:11:55 -0700 Subject: [PATCH 2/3] Add common/perf-test-core to template ci.yml --- sdk/template/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/template/ci.yml b/sdk/template/ci.yml index e63e7c2fa88b..90af8fea53d6 100644 --- a/sdk/template/ci.yml +++ b/sdk/template/ci.yml @@ -24,6 +24,8 @@ pr: paths: include: - sdk/template/ + # The following path should only be included in template/ci.yml, and removed from any other SDKs which copy this file. + - common/perf-test-core/ exclude: - sdk/template/pom.xml - sdk/template/azure-sdk-template/pom.xml From 7f34061356498679100ef08dcf70073a43bd2429 Mon Sep 17 00:00:00 2001 From: Mike Harder Date: Thu, 7 Oct 2021 14:01:45 -0700 Subject: [PATCH 3/3] Convert recordAndStartPlaybackAsync() to sync --- .../perf/test/core/PerfStressProgram.java | 13 ++++++- .../azure/perf/test/core/PerfStressTest.java | 38 +++++++++---------- 2 files changed, 30 insertions(+), 21 deletions(-) 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..1f71fb81f5ff 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 @@ -143,7 +143,18 @@ public static void run(Class testClass, PerfStressOptions options) { if (options.getTestProxy() != null) { Disposable recordStatus = printStatus("=== Record and Start Playback ===", () -> ".", false, false); - Flux.just(tests).flatMap(PerfStressTest::recordAndStartPlaybackAsync).blockLast(); + + try { + ForkJoinPool forkJoinPool = new ForkJoinPool(tests.length); + forkJoinPool.submit(() -> { + IntStream.range(0, tests.length).parallel().forEach(i -> tests[i].recordAndStartPlayback()); + }).get(); + } catch (InterruptedException | ExecutionException e) { + System.err.println("Error occurred when submitting jobs to ForkJoinPool. " + System.lineSeparator() + e); + e.printStackTrace(System.err); + throw new RuntimeException(e); + } + startedPlayback = true; recordStatus.dispose(); } 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..4f8be4d18de9 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 @@ -128,32 +128,30 @@ public Mono setupAsync() { } /** - * Records responses and starts async tests in playback mode. - * @return An empty {@link Mono}. + * Records responses and starts tests in playback mode. */ - public Mono recordAndStartPlaybackAsync() { + public void recordAndStartPlayback() { // Make one call to Run() before starting recording, to avoid capturing one-time setup like authorization requests. - return runSyncOrAsync() - .then(startRecordingAsync()) - .doOnSuccess(x -> { - testProxyPolicy.setRecordingId(recordingId); - testProxyPolicy.setMode("record"); - }) - // Must use Mono.defer() to ensure fields are set from prior requests - .then(Mono.defer(() -> runSyncOrAsync())) - .then(Mono.defer(() -> stopRecordingAsync())) - .then(Mono.defer(() -> startPlaybackAsync())) - .doOnSuccess(x -> { - testProxyPolicy.setRecordingId(recordingId); - testProxyPolicy.setMode("playback"); - }); + runSyncOrAsync(); + + startRecordingAsync().block(); + + testProxyPolicy.setRecordingId(recordingId); + testProxyPolicy.setMode("record"); + + runSyncOrAsync(); + stopRecordingAsync().block(); + startPlaybackAsync().block(); + + testProxyPolicy.setRecordingId(recordingId); + testProxyPolicy.setMode("playback"); } - private Mono runSyncOrAsync() { + private void runSyncOrAsync() { if (options.isSync()) { - return Mono.empty().then().doOnSuccess(x -> run()); + run(); } else { - return runAsync(); + runAsync().block(); } }