Skip to content

Commit 882ae75

Browse files
authored
[Perf] Support multiple test proxies (Azure#21203)
- Some scenarios require multiple test-proxy URLs to bottleneck client - test-proxy URLs are assigned round-robin to parallel test instances - Ported from Azure/azure-sdk-for-net#24265
1 parent 375337f commit 882ae75

File tree

6 files changed

+24
-14
lines changed

6 files changed

+24
-14
lines changed

doc/dev/perfstress_tests.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ The framework has a series of common command line options built in:
8282
- `-w --warm-up=5` Number of seconds to spend warming up the connection before measuring begins. Default is 5.
8383
- `--sync` Whether to run the tests in sync or async. Default is False (async).
8484
- `--no-cleanup` Whether to keep newly created resources after test run. Default is False (resources will be deleted).
85-
- `-x --test-proxy` Whether to run the tests against the test proxy server. Specfiy the URL for the proxy endpoint (e.g. "https://localhost:5001").
85+
- `-x --test-proxies` Whether to run the tests against the test proxy server. Specify the URL(s) for the proxy endpoint(s) (e.g. "https://localhost:5001").
8686
- `--profile` Whether to run the perftest with cProfile. If enabled (default is False), the output file of the **last completed single iteration** will be written to the current working directory in the format `"cProfile-<TestClassName>-<TestID>-<sync/async>.pstats"`.
8787

8888

sdk/storage/azure-storage-blob/tests/perfstress_tests/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ These options are available for all perf tests:
4444
- `-w --warm-up=5` Number of seconds to spend warming up the connection before measuring begins. Default is 5.
4545
- `--sync` Whether to run the tests in sync or async. Default is False (async). This flag must be used for Storage legacy tests, which do not support async.
4646
- `--no-cleanup` Whether to keep newly created resources after test run. Default is False (resources will be deleted).
47-
- `-x --test-proxy` Whether to run the tests against the test proxy server. Specfiy the URL for the proxy endpoint (e.g. "https://localhost:5001"). WARNING: When using with Legacy tests - only HTTPS is supported.
47+
- `-x --test-proxies` Whether to run the tests against the test proxy server. Specfiy the URL(s) for the proxy endpoint(s) (e.g. "https://localhost:5001"). WARNING: When using with Legacy tests - only HTTPS is supported.
4848
- `--profile` Whether to run the perftest with cProfile. If enabled (default is False), the output file of the **last completed single iteration** will be written to the current working directory in the format `"cProfile-<TestClassName>-<TestID>-<sync/async>.pstats"`.
4949

5050
### Common Blob command line options

sdk/storage/azure-storage-blob/tests/perfstress_tests/T1_legacy_tests/_test_base_legacy.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def __init__(self, arguments):
3737
super().__init__(arguments)
3838
connection_string = self.get_from_env("AZURE_STORAGE_CONNECTION_STRING")
3939
session = None
40-
if self.args.test_proxy:
40+
if self.args.test_proxies:
4141
session = requests.Session()
4242
session.verify = False
4343
if not _LegacyServiceTest.service_client or self.args.no_client_share:
@@ -50,7 +50,7 @@ def __init__(self, arguments):
5050
self.async_service_client = None
5151
self.service_client = _LegacyServiceTest.service_client
5252

53-
if self.args.test_proxy:
53+
if self.args.test_proxies:
5454
self.service_client.request_callback = functools.partial(
5555
test_proxy_callback,
5656
self._test_proxy_policy

sdk/storage/azure-storage-blob/tests/perfstress_tests/_test_base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class _ServiceTest(PerfStressTest):
2020
def __init__(self, arguments):
2121
super().__init__(arguments)
2222
connection_string = self.get_from_env("AZURE_STORAGE_CONNECTION_STRING")
23-
if self.args.test_proxy:
23+
if self.args.test_proxies:
2424
self._client_kwargs['_additional_pipeline_policies'] = self._client_kwargs['per_retry_policies']
2525
self._client_kwargs['max_single_put_size'] = self.args.max_put_size
2626
self._client_kwargs['max_block_size'] = self.args.max_block_size

tools/azure-devtools/src/azure_devtools/perfstress_tests/perf_stress_runner.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ def _parse_args(self):
9797
"--profile", action="store_true", help="Run tests with profiler. Default is False.", default=False
9898
)
9999
per_test_arg_parser.add_argument(
100-
"-x", "--test-proxy", help="URI of TestProxy Server"
100+
"-x", "--test-proxies", help="URIs of TestProxy Servers (separated by ';')",
101+
type=lambda s: s.split(';')
101102
)
102103

103104
# Per-test args
@@ -142,7 +143,7 @@ async def start(self):
142143
await asyncio.gather(*[test.setup() for test in tests])
143144
self.logger.info("")
144145

145-
if self.per_test_args.test_proxy:
146+
if self.per_test_args.test_proxies:
146147
self.logger.info("=== Record and Start Playback ===")
147148
await asyncio.gather(*[test.record_and_start_playback() for test in tests])
148149
self.logger.info("")
@@ -162,7 +163,7 @@ async def start(self):
162163
except Exception as e:
163164
print("Exception: " + str(e))
164165
finally:
165-
if self.per_test_args.test_proxy:
166+
if self.per_test_args.test_proxies:
166167
self.logger.info("=== Stop Playback ===")
167168
await asyncio.gather(*[test.stop_playback() for test in tests])
168169
self.logger.info("")

tools/azure-devtools/src/azure_devtools/perfstress_tests/perf_stress_test.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# --------------------------------------------------------------------------------------------
55

66
import os
7+
import threading
78
import aiohttp
89

910
from urllib.parse import urljoin
@@ -21,15 +22,22 @@ class PerfStressTest:
2122
"""
2223

2324
args = {}
25+
_global_parallel_index_lock = threading.Lock()
26+
_global_parallel_index = 0
2427

2528
def __init__(self, arguments):
2629
self.args = arguments
2730
self._session = None
31+
self._test_proxy = None
2832
self._test_proxy_policy = None
2933
self._client_kwargs = {}
3034
self._recording_id = None
3135

32-
if self.args.test_proxy:
36+
with PerfStressTest._global_parallel_index_lock:
37+
self._parallel_index = PerfStressTest._global_parallel_index
38+
PerfStressTest._global_parallel_index += 1
39+
40+
if self.args.test_proxies:
3341
self._session = aiohttp.ClientSession(connector=aiohttp.TCPConnector(verify_ssl=False))
3442

3543
# SSL will be disabled for the test proxy requests, so suppress warnings
@@ -38,7 +46,8 @@ def __init__(self, arguments):
3846
warnings.simplefilter('ignore', InsecureRequestWarning)
3947

4048
# Add policy to redirect requests to the test proxy
41-
self._test_proxy_policy = PerfTestProxyPolicy(self.args.test_proxy)
49+
self._test_proxy = self.args.test_proxies[self._parallel_index % len(self.args.test_proxies)]
50+
self._test_proxy_policy = PerfTestProxyPolicy(self._test_proxy)
4251
self._client_kwargs['per_retry_policies'] = [self._test_proxy_policy]
4352

4453
async def global_setup(self):
@@ -74,7 +83,7 @@ async def stop_playback(self):
7483
"x-recording-id": self._recording_id,
7584
"x-purge-inmemory-recording": "true"
7685
}
77-
url = urljoin(self.args.test_proxy, "/playback/stop")
86+
url = urljoin(self._test_proxy, "/playback/stop")
7887
async with self._session.post(url, headers=headers) as resp:
7988
assert resp.status == 200
8089

@@ -98,20 +107,20 @@ async def run_async(self):
98107
raise Exception("run_async must be implemented for {}".format(self.__class__.__name__))
99108

100109
async def _start_recording(self):
101-
url = urljoin(self.args.test_proxy, "/record/start")
110+
url = urljoin(self._test_proxy, "/record/start")
102111
async with self._session.post(url) as resp:
103112
assert resp.status == 200
104113
self._recording_id = resp.headers["x-recording-id"]
105114

106115
async def _stop_recording(self):
107116
headers = {"x-recording-id": self._recording_id}
108-
url = urljoin(self.args.test_proxy, "/record/stop")
117+
url = urljoin(self._test_proxy, "/record/stop")
109118
async with self._session.post(url, headers=headers) as resp:
110119
assert resp.status == 200
111120

112121
async def _start_playback(self):
113122
headers = {"x-recording-id": self._recording_id}
114-
url = urljoin(self.args.test_proxy, "/playback/start")
123+
url = urljoin(self._test_proxy, "/playback/start")
115124
async with self._session.post(url, headers=headers) as resp:
116125
assert resp.status == 200
117126
self._recording_id = resp.headers["x-recording-id"]

0 commit comments

Comments
 (0)