diff --git a/sdk/test-utils/perfstress/CHANGELOG.md b/sdk/test-utils/perfstress/CHANGELOG.md index 53b38c0cd57e..06b7266c85e9 100644 --- a/sdk/test-utils/perfstress/CHANGELOG.md +++ b/sdk/test-utils/perfstress/CHANGELOG.md @@ -2,6 +2,11 @@ ## 1.0.0 (Unreleased) +### 2021-10-05 + +- Support multiple test proxies + [#18031](https://github.com/Azure/azure-sdk-for-js/pull/18031) + ### 2021-10-01 - Calls runAsync() once before starting recording, to avoid capturing one-time setup like authorization requests. diff --git a/sdk/test-utils/perfstress/GettingStarted.md b/sdk/test-utils/perfstress/GettingStarted.md index fb6ac5495dde..9d47b72f7a33 100644 --- a/sdk/test-utils/perfstress/GettingStarted.md +++ b/sdk/test-utils/perfstress/GettingStarted.md @@ -291,21 +291,21 @@ Run this command Reference: https://github.com/Azure/azure-sdk-tools/tree/main/tools/test-proxy/Azure.Sdk.Tools.TestProxy#via-docker-image -To use the proxy-tool in your test pass this option in cli `--test-proxy http://localhost:5000`(Make sure the port is same as what you have used to run the `docker run` command). +To use the proxy-tool in your test pass this option in cli `--test-proxies http://localhost:5000`(Make sure the port is same as what you have used to run the `docker run` command). Sample command(using storage-blob perf tests as example (Core-v1)!) -> npm run perf-test:node -- StorageBlobDownloadTest --warmup 2 --duration 7 --iterations 2 --test-proxy http://localhost:5000 +> npm run perf-test:node -- StorageBlobDownloadTest --warmup 2 --duration 7 --iterations 2 --test-proxies http://localhost:5000 -> npm run perf-test:node -- StorageBlobDownloadTest --warmup 2 --duration 7 --iterations 2 --parallel 2 --test-proxy http://localhost:5000 +> npm run perf-test:node -- StorageBlobDownloadTest --warmup 2 --duration 7 --iterations 2 --parallel 2 --test-proxies http://localhost:5000 -> npm run perf-test:node -- StorageBlobDownloadTest --warmup 2 --duration 7 --iterations 2 --parallel 2 --test-proxy https://localhost:5001 --insecure true +> npm run perf-test:node -- StorageBlobDownloadTest --warmup 2 --duration 7 --iterations 2 --parallel 2 --test-proxies https://localhost:5001 --insecure true Sample command(using data-tables perf tests as example (Core-v2)!) -> npm run perf-test:node -- ListComplexEntitiesTest --duration 7 --iterations 2 --parallel 2 --test-proxy http://localhost:5000 +> npm run perf-test:node -- ListComplexEntitiesTest --duration 7 --iterations 2 --parallel 2 --test-proxies http://localhost:5000 -> npm run perf-test:node -- ListComplexEntitiesTest --duration 7 --iterations 2 --parallel 2 --test-proxy https://localhost:5001 --insecure true +> npm run perf-test:node -- ListComplexEntitiesTest --duration 7 --iterations 2 --parallel 2 --test-proxies https://localhost:5001 --insecure true > npm run perf-test:node -- ListComplexEntitiesTest --duration 7 --iterations 2 --parallel 2 diff --git a/sdk/test-utils/perfstress/README.md b/sdk/test-utils/perfstress/README.md index d5cbd7d3d3cd..b9812bf4631e 100644 --- a/sdk/test-utils/perfstress/README.md +++ b/sdk/test-utils/perfstress/README.md @@ -15,7 +15,7 @@ Link to the wiki - [Writing-Performance-Tests](https://github.com/Azure/azure-sd - PerfStress tests are executed as many times as possible until the `duration` parameter is specified. This process may repeat as many `iterations` are given. Before each iteration, tests might be called for a period of time up to `warmup`, to adjust to possible runtime optimizations. In each iteration, as many as `parallel` instances of the same test are called without waiting for each other, letting the event loop decide which one is prioritized (it's not true parallelism, but it's an approximation that aligns with the design in other languages, we might improve it over time). - Each test can have a `globalSetup` method, which is called once before the process begins, a `globalCleanup` method, which is called once after the process finishes. - Each test can have a `setup` method, which is called as many times as test instances are created (up to `parallel`), and help specify local state for each test instance. A `cleanup` method is also optional, called the same amount of times, but after finishing running the tests. -- `test-proxy` url option - this option can be leveraged to avoid hitting throttling scenarios while testing the services. This option lets the requests go through the proxy server based on the url provided, we run runAsync method once in record mode to save the requests and responses in memory and then a ton of times in playback. Workflow with the test-proxy below. +- `test-proxies` url option - this option can be leveraged to avoid hitting throttling scenarios while testing the services. This option lets the requests go through proxy server(s) based on the url(s) provided, we run runAsync method once in record mode to save the requests and responses in memory and then a ton of times in playback. Workflow with the test-proxies below. ## Workflow with test proxy diff --git a/sdk/test-utils/perfstress/src/options.ts b/sdk/test-utils/perfstress/src/options.ts index 89745dbbcce5..36bf2cfc82f9 100644 --- a/sdk/test-utils/perfstress/src/options.ts +++ b/sdk/test-utils/perfstress/src/options.ts @@ -66,7 +66,7 @@ export interface DefaultPerfStressOptions { iterations: number; "no-cleanup": boolean; "milliseconds-to-log": number; - "test-proxy": string; + "test-proxies": string; insecure: boolean; } @@ -101,13 +101,13 @@ export const defaultPerfStressOptions: PerfStressOptionDictionary this.recordAndStartPlayback(test))); @@ -301,7 +301,7 @@ export class PerfStressProgram { await this.runTest(i, Number(options.duration.value), "test"); } - if (this.tests[0].parsedOptions["test-proxy"].value) { + if (this.tests[0].parsedOptions["test-proxies"].value) { await Promise.all(this.tests.map((test) => this.stopPlayback(test))); } diff --git a/sdk/test-utils/perfstress/src/tests.ts b/sdk/test-utils/perfstress/src/tests.ts index 4504e87a72dd..fe5c97ccd11a 100644 --- a/sdk/test-utils/perfstress/src/tests.ts +++ b/sdk/test-utils/perfstress/src/tests.ts @@ -34,10 +34,25 @@ export interface PerfStressTestConstructor { * (initializations are as many as the "parallel" command line parameter specifies). */ export abstract class PerfStressTest { + private readonly testProxy!: string; public testProxyHttpClient!: TestProxyHttpClient; public testProxyHttpClientV1!: TestProxyHttpClientV1; public abstract options: PerfStressOptionDictionary; + private static globalParallelIndex: number = 0; + protected readonly parallelIndex: number; + + public constructor() { + this.parallelIndex = PerfStressTest.globalParallelIndex; + PerfStressTest.globalParallelIndex++; + + const testProxies = this.parsedOptions["test-proxies"].value; + if (testProxies) { + const testProxiesArray = testProxies.split(";"); + this.testProxy = testProxiesArray[this.parallelIndex % testProxiesArray.length]; + } + } + public get parsedOptions(): PerfStressOptionDictionary { // This cast is needed because TS thinks // PerfStressOptionDictionary @@ -67,9 +82,9 @@ export abstract class PerfStressTest { * Note: httpClient must be part of the options bag, it is required for the perf framework to update the underlying client properly */ public configureClientOptionsCoreV1(options: T & { httpClient?: HttpClient }): T { - if (this.parsedOptions["test-proxy"].value) { + if (this.testProxy) { this.testProxyHttpClientV1 = new TestProxyHttpClientV1( - this.parsedOptions["test-proxy"].value, + this.testProxy, this.parsedOptions["insecure"].value! ); options.httpClient = this.testProxyHttpClientV1; @@ -86,16 +101,15 @@ export abstract class PerfStressTest { * Note: Client must expose the pipeline property which is required for the perf framework to add its policies correctly */ public configureClient(client: T & { pipeline: Pipeline }): T { - const url = this.parsedOptions["test-proxy"].value; - if (url) { + if (this.testProxy) { this.testProxyHttpClient = new TestProxyHttpClient( - url, + this.testProxy, this.parsedOptions["insecure"].value! ); client.pipeline.addPolicy( testProxyHttpPolicy( this.testProxyHttpClient, - url.startsWith("https"), + this.testProxy.startsWith("https"), this.parsedOptions["insecure"].value! ) );