summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Eissing <icing@apache.org>2021-12-01 15:03:42 +0100
committerStefan Eissing <icing@apache.org>2021-12-01 15:03:42 +0100
commit4d8f1f96b6e6e47a822a8e0d4c3f59d76b38ee87 (patch)
tree8d5e0cf346d4dd4b8dc1ac0eae0a6bda2aaac9b9
parent * travis: installing librustls in $PREFIX and using (diff)
downloadapache2-4d8f1f96b6e6e47a822a8e0d4c3f59d76b38ee87.tar.xz
apache2-4d8f1f96b6e6e47a822a8e0d4c3f59d76b38ee87.zip
* test: removing the loadtest.py from test/modules/tls that requires additional python packages.
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1895467 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--test/modules/tls/load_test.py882
1 files changed, 0 insertions, 882 deletions
diff --git a/test/modules/tls/load_test.py b/test/modules/tls/load_test.py
deleted file mode 100644
index d0fba3385a..0000000000
--- a/test/modules/tls/load_test.py
+++ /dev/null
@@ -1,882 +0,0 @@
-import argparse
-import logging
-import multiprocessing
-import os
-import re
-import sys
-import time
-from datetime import timedelta, datetime
-from threading import Thread
-from tqdm import tqdm # type: ignore
-from typing import Dict, Iterable, List, Tuple, Optional
-
-sys.path.append(os.path.dirname(__file__))
-sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
-
-from conf import TlsTestConf
-from env import TlsTestEnv, TlsTestSetup
-from pyhttpd.result import ExecResult
-
-log = logging.getLogger(__name__)
-
-
-class LoadTestException(Exception):
- pass
-
-
-class H2LoadLogSummary:
-
- @staticmethod
- def from_file(fpath: str, title: str, duration: timedelta) -> 'H2LoadLogSummary':
- with open(fpath) as fd:
- return H2LoadLogSummary.from_lines(fd.readlines(), title=title, duration=duration)
-
- @staticmethod
- def from_lines(lines: Iterable[str], title: str, duration: timedelta) -> 'H2LoadLogSummary':
- stati = {}
- count = 0
- all_durations = timedelta(milliseconds=0)
- for line in lines:
- parts = re.split(r'\s+', line) # start(us), status(int), duration(ms), tbd.
- if len(parts) >= 3 and parts[0] and parts[1] and parts[2]:
- count += 1
- status = int(parts[1])
- if status in stati:
- stati[status] += 1
- else:
- stati[status] = 1
- all_durations += timedelta(microseconds=int(parts[2]))
- else:
- sys.stderr.write("unrecognize log line: {0}".format(line))
- return H2LoadLogSummary(title=title, total=count, stati=stati,
- duration=duration, all_durations=all_durations)
-
- def __init__(self, title: str, total: int, stati: Dict[int, int],
- duration: timedelta, all_durations: timedelta):
- self._title = title
- self._total = total
- self._stati = stati
- self._duration = duration
- self._all_durations = all_durations
- self._transfered_mb = 0.0
- self._exec_result = None
- self._expected_responses = 0
-
- @property
- def title(self) -> str:
- return self._title
-
- @property
- def response_count(self) -> int:
- return self._total
-
- @property
- def duration(self) -> timedelta:
- return self._duration
-
- @property
- def response_durations(self) -> timedelta:
- return self._all_durations
-
- @property
- def response_stati(self) -> Dict[int, int]:
- return self._stati
-
- @property
- def expected_responses(self) -> int:
- return self._expected_responses
-
- @property
- def execution(self) -> ExecResult:
- return self._exec_result
-
- def all_200(self) -> bool:
- non_200s = [n for n in self._stati.keys() if n != 200]
- return len(non_200s) == 0
-
- @property
- def throughput_mb(self) -> float:
- if self._transfered_mb > 0.0:
- return self._transfered_mb / self.duration.total_seconds()
- return 0.0
-
- def set_transfered_mb(self, mb: float) -> None:
- self._transfered_mb = mb
-
- def set_exec_result(self, result: ExecResult):
- self._exec_result = result
-
- def set_expected_responses(self, n: int):
- self._expected_responses = n
-
- def get_footnote(self) -> Optional[str]:
- note = ""
- if 0 < self.expected_responses != self.response_count:
- note += "{0}/{1} missing".format(
- self.expected_responses - self.response_count,
- self.expected_responses
- )
- if not self.all_200():
- note += ", non 200s:"
- for status in [n for n in self.response_stati.keys() if n != 200]:
- note += " {0}={1}".format(status, self.response_stati[status])
- return note if len(note) else None
-
-
-class H2LoadMonitor:
-
- def __init__(self, fpath: str, expected: int, title: str):
- self._fpath = fpath
- self._expected = expected
- self._title = title
- self._tqdm = tqdm(desc=title, total=expected, unit="request", leave=False)
- self._running = False
- self._lines = ()
- self._tail = None
-
- def start(self):
- self._tail = Thread(target=self._collect, kwargs={'self': self})
- self._running = True
- self._tail.start()
-
- def get_summary(self, duration: timedelta) -> H2LoadLogSummary:
- self._running = False
- self._tail.join()
- return H2LoadLogSummary.from_file(self._fpath, title=self._title, duration=duration)
-
- def stop(self):
- self._running = False
-
- @staticmethod
- def _collect(self) -> None:
- first_call = True
- while self._running:
- try:
- with open(self._fpath) as fd:
- if first_call:
- fd.seek(0, 2)
- first_call = False
- latest_data = fd.read()
- while self._running:
- if '\n' not in latest_data:
- latest_data += fd.read()
- if '\n' not in latest_data:
- if not os.path.isfile(self._fpath):
- break
- time.sleep(0.1)
- continue
- lines = latest_data.split('\n')
- if lines[-1] != '\n':
- latest_data = lines[-1]
- lines = lines[:-1]
- else:
- latest_data = None
- self._tqdm.update(n=len(lines))
- if latest_data is None:
- latest_data = fd.read()
- except IOError:
- time.sleep(0.1)
- self._tqdm.close()
-
-
-def mk_text_file(fpath: str, lines: int):
- t110 = ""
- for _ in range(11):
- t110 += "0123456789"
- with open(fpath, "w") as fd:
- for i in range(lines):
- fd.write("{0:015d}: ".format(i)) # total 128 bytes per line
- fd.write(t110)
- fd.write("\n")
-
-
-class LoadTestCase:
-
- @staticmethod
- def from_scenario(scenario: Dict, env: TlsTestEnv) -> 'SingleFileLoadTest':
- raise NotImplemented
-
- def run(self) -> H2LoadLogSummary:
- raise NotImplemented
-
- def format_result(self, summary: H2LoadLogSummary) -> str:
- raise NotImplemented
-
- @staticmethod
- def setup_base_conf(env: TlsTestEnv, worker_count: int = 5000, extras=None) -> TlsTestConf:
- conf = TlsTestConf(env=env, extras=extras)
- # ylavic's formula
- process_count = int(max(10, min(100, int(worker_count / 100))))
- thread_count = int(max(25, int(worker_count / process_count)))
- conf.add(f"""
- StartServers 1
- ServerLimit {int(process_count * 2.5)}
- ThreadLimit {thread_count}
- ThreadsPerChild {thread_count}
- MinSpareThreads {thread_count}
- MaxSpareThreads {int(worker_count / 2)}
- MaxRequestWorkers {worker_count}
- MaxConnectionsPerChild 0
- KeepAliveTimeout 60
- MaxKeepAliveRequests 0
- """)
- return conf
-
- @staticmethod
- def start_server(env: TlsTestEnv, cd: timedelta = None):
- if cd:
- with tqdm(desc="connection cooldown", total=int(cd.total_seconds()), unit="s", leave=False) as t:
- end = datetime.now() + cd
- while datetime.now() < end:
- time.sleep(1)
- t.update()
- assert env.apache_restart() == 0
-
- @staticmethod
- def server_setup(env: TlsTestEnv, ssl_module: str):
- if 'mod_tls' == ssl_module:
- extras = {
- 'base': [
- "Protocols h2 http/1.1",
- "ProxyPreserveHost on",
- f"TLSProxyCA {env.ca.cert_file}",
- f"<Proxy https://127.0.0.1:{env.https_port}/>",
- " TLSProxyEngine on",
- "</Proxy>",
- f"<Proxy h2://127.0.0.1:{env.https_port}/>",
- " TLSProxyEngine on",
- "</Proxy>",
- ],
- env.domain_a: [
- f"ProxyPass /proxy-h1/ https://127.0.0.1:{env.https_port}/",
- f"ProxyPass /proxy-h2/ h2://127.0.0.1:{env.https_port}/",
- f"TLSOptions +StdEnvVars",
- ],
- }
- elif 'mod_ssl' == ssl_module:
- extras = {
- 'base': [
- "Protocols h2 http/1.1",
- "ProxyPreserveHost on",
- "SSLProxyVerify require",
- f"SSLProxyCACertificateFile {env.ca.cert_file}",
- f"<Proxy https://127.0.0.1:{env.https_port}/>",
- " SSLProxyEngine on",
- "</Proxy>",
- f"<Proxy h2://127.0.0.1:{env.https_port}/>",
- " SSLProxyEngine on",
- "</Proxy>",
- ],
- env.domain_a: [
- f"ProxyPass /proxy-h1/ https://127.0.0.1:{env.https_port}/",
- f"ProxyPass /proxy-h2/ h2://127.0.0.1:{env.https_port}/",
- "TLSOptions +StdEnvVars",
- ],
- }
- elif 'mod_gnutls' == ssl_module:
- extras = {
- 'base': [
- "Protocols h2 http/1.1",
- "ProxyPreserveHost on",
- f"GnuTLSProxyCAFile {env.ca.cert_file}",
- f"<Proxy https://127.0.0.1:{env.https_port}/>",
- " GnuTLSProxyEngine on",
- "</Proxy>",
- f"<Proxy h2://127.0.0.1:{env.https_port}/>",
- " GnuTLSProxyEngine on",
- "</Proxy>",
- ],
- env.domain_a: [
- f"ProxyPass /proxy-h1/ https://127.0.0.1:{env.https_port}/",
- f"ProxyPass /proxy-h2/ h2://127.0.0.1:{env.https_port}/",
- ],
- }
- else:
- raise LoadTestException("tests for module: {0}".format(ssl_module))
- conf = LoadTestCase.setup_base_conf(env=env, extras=extras)
- conf.add_tls_vhosts(domains=[env.domain_a], ssl_module=ssl_module)
- conf.install()
-
-
-class SingleFileLoadTest(LoadTestCase):
-
- def __init__(self, env: TlsTestEnv, location: str,
- clients: int, requests: int, resource_kb: int,
- ssl_module: str = 'mod_tls', protocol: str = 'h2',
- threads: int = None):
- self.env = env
- self._location = location
- self._clients = clients
- self._requests = requests
- self._resource_kb = resource_kb
- self._ssl_module = ssl_module
- self._protocol = protocol
- self._threads = threads if threads is not None else min(multiprocessing.cpu_count() / 2, self._clients)
-
- @staticmethod
- def from_scenario(scenario: Dict, env: TlsTestEnv) -> 'SingleFileLoadTest':
- return SingleFileLoadTest(
- env=env,
- location=scenario['location'],
- clients=scenario['clients'], requests=scenario['requests'],
- ssl_module=scenario['module'], resource_kb=scenario['rsize'],
- protocol=scenario['protocol'] if 'protocol' in scenario else 'h2'
- )
-
- def _setup(self) -> str:
- LoadTestCase.server_setup(env=self.env, ssl_module=self._ssl_module)
- docs_a = os.path.join(self.env.server_docs_dir, self.env.domain_a)
- fname = "{0}k.txt".format(self._resource_kb)
- mk_text_file(os.path.join(docs_a, fname), 8 * self._resource_kb)
- self.start_server(env=self.env)
- return fname
-
- def _teardown(self):
- pass
-
- def run_test(self, mode: str, path: str) -> H2LoadLogSummary:
- monitor = None
- try:
- log_file = "{gen_dir}/h2load.log".format(gen_dir=self.env.gen_dir)
- if os.path.isfile(log_file):
- os.remove(log_file)
- monitor = H2LoadMonitor(log_file, expected=self._requests,
- title=f"{self._ssl_module}/{self._protocol}/"
- f"{self._clients}c/{self._resource_kb / 1024}MB[{mode}]")
- monitor.start()
- args = [
- 'h2load',
- '--clients={0}'.format(self._clients),
- '--threads={0}'.format(self._threads),
- '--requests={0}'.format(self._requests),
- '--log-file={0}'.format(log_file),
- '--connect-to=localhost:{0}'.format(self.env.https_port)
- ]
- if self._protocol == 'h1' or self._protocol == 'http/1.1':
- args.append('--h1')
- elif self._protocol == 'h2':
- args.extend(['-m', "6"])
- else:
- raise Exception(f"unknown protocol: {self._protocol}")
- r = self.env.run(args + [
- f'https://{self.env.domain_a}:{self.env.https_port}{self._location}{path}'
- ])
- if r.exit_code != 0:
- raise LoadTestException("h2load returned {0}: {1}".format(r.exit_code, r.stderr))
- summary = monitor.get_summary(duration=r.duration)
- summary.set_expected_responses(self._requests)
- summary.set_exec_result(r)
- summary.set_transfered_mb(self._requests * self._resource_kb / 1024)
- return summary
- finally:
- if monitor is not None:
- monitor.stop()
-
- def run(self) -> H2LoadLogSummary:
- path = self._setup()
- try:
- self.run_test(mode="warmup", path=path)
- return self.run_test(mode="measure", path=path)
- finally:
- self._teardown()
-
- def format_result(self, summary: H2LoadLogSummary) -> Tuple[str, Optional[List[str]]]:
- return "{0:.0f}".format(summary.throughput_mb), summary.get_footnote()
-
-
-class MultiFileLoadTest(LoadTestCase):
- SETUP_DONE = False
-
- def __init__(self, env: TlsTestEnv, location: str,
- clients: int, requests: int, file_count: int,
- file_sizes: List[int],
- ssl_module: str = 'mod_tls', protocol: str = 'h2',
- threads: int = None, ):
- self.env = env
- self._location = location
- self._clients = clients
- self._requests = requests
- self._file_count = file_count
- self._file_sizes = file_sizes
- self._ssl_module = ssl_module
- self._protocol = protocol
- self._threads = threads if threads is not None else \
- min(multiprocessing.cpu_count() / 2, self._clients)
- self._url_file = "{gen_dir}/h2load-urls.txt".format(gen_dir=self.env.gen_dir)
-
- @staticmethod
- def from_scenario(scenario: Dict, env: TlsTestEnv) -> 'MultiFileLoadTest':
- return MultiFileLoadTest(
- env=env,
- location=scenario['location'],
- clients=scenario['clients'], requests=scenario['requests'],
- file_sizes=scenario['file_sizes'], file_count=scenario['file_count'],
- ssl_module=scenario['module'], protocol=scenario['protocol']
- )
-
- def _setup(self, cls):
- LoadTestCase.server_setup(env=self.env, ssl_module=self._ssl_module)
- if not cls.SETUP_DONE:
- with tqdm(desc="setup resources", total=self._file_count, unit="file", leave=False) as t:
- docs_a = os.path.join(self.env.server_docs_dir, self.env.domain_a)
- uris = []
- for i in range(self._file_count):
- fsize = self._file_sizes[i % len(self._file_sizes)]
- if fsize is None:
- raise Exception("file sizes?: {0} {1}".format(i, fsize))
- fname = "{0}-{1}k.txt".format(i, fsize)
- mk_text_file(os.path.join(docs_a, fname), 8 * fsize)
- uris.append(f"{self._location}{fname}")
- t.update()
- with open(self._url_file, 'w') as fd:
- fd.write("\n".join(uris))
- fd.write("\n")
- cls.SETUP_DONE = True
- self.start_server(env=self.env)
-
- def _teardown(self):
- pass
-
- def run_test(self, mode: str, path: str) -> H2LoadLogSummary:
- _path = path
- monitor = None
- try:
- log_file = "{gen_dir}/h2load.log".format(gen_dir=self.env.gen_dir)
- if os.path.isfile(log_file):
- os.remove(log_file)
- monitor = H2LoadMonitor(log_file, expected=self._requests,
- title=f"{self._ssl_module}/{self._protocol}/"
- f"{self._file_count / 1024}f/{self._clients}c[{mode}]")
- monitor.start()
- args = [
- 'h2load',
- '--clients={0}'.format(self._clients),
- '--requests={0}'.format(self._requests),
- '--input-file={0}'.format(self._url_file),
- '--log-file={0}'.format(log_file),
- '--connect-to=localhost:{0}'.format(self.env.https_port)
- ]
- if self._protocol == 'h1' or self._protocol == 'http/1.1':
- args.append('--h1')
- elif self._protocol == 'h2':
- args.extend(['-m', "6"])
- else:
- raise Exception(f"unknown protocol: {self._protocol}")
- r = self.env.run(args + [
- f'--base-uri=https://{self.env.domain_a}:{self.env.https_port}{self._location}'
- ])
- if r.exit_code != 0:
- raise LoadTestException("h2load returned {0}: {1}".format(r.exit_code, r.stderr))
- summary = monitor.get_summary(duration=r.duration)
- summary.set_expected_responses(self._requests)
- summary.set_exec_result(r)
- return summary
- finally:
- if monitor is not None:
- monitor.stop()
-
- def run(self) -> H2LoadLogSummary:
- path = self._setup(self.__class__)
- try:
- time.sleep(1)
- self.run_test(mode="warmup", path=path)
- return self.run_test(mode="measure", path=path)
- finally:
- self._teardown()
-
- def format_result(self, summary: H2LoadLogSummary) -> Tuple[str, Optional[List[str]]]:
- return "{0:.0f}".format(
- summary.response_count / summary.duration.total_seconds()
- ), summary.get_footnote()
-
-
-class ConnectionLoadTest(LoadTestCase):
- SETUP_DONE = False
-
- def __init__(self, env: TlsTestEnv, location: str,
- clients: int, requests: int, duration: timedelta,
- file_count: int, file_sizes: List[int], cooldown: timedelta,
- ssl_module: str = 'mod_tls', protocol: str = 'h2'):
- self.env = env
- self._location = location
- self._clients = clients
- self._requests = requests
- self._duration = duration
- self._file_count = file_count
- self._file_sizes = file_sizes
- self._ssl_module = ssl_module
- self._protocol = protocol
- self._url_file = "{gen_dir}/h2load-urls.txt".format(gen_dir=self.env.gen_dir)
- self._cd = cooldown
-
- @staticmethod
- def from_scenario(scenario: Dict, env: TlsTestEnv) -> 'ConnectionLoadTest':
- return ConnectionLoadTest(
- env=env,
- location=scenario['location'],
- clients=scenario['clients'], requests=scenario['requests'],
- duration=scenario['duration'], cooldown=scenario['cooldown'],
- file_sizes=scenario['file_sizes'], file_count=scenario['file_count'],
- ssl_module=scenario['module'], protocol=scenario['protocol']
- )
-
- def _setup(self):
- LoadTestCase.server_setup(env=self.env, ssl_module=self._ssl_module)
- if not ConnectionLoadTest.SETUP_DONE:
- with tqdm(desc="setup resources", total=self._file_count, unit="file", leave=False) as t:
- docs_a = os.path.join(self.env.server_docs_dir, self.env.domain_a)
- uris = []
- for i in range(self._file_count):
- fsize = self._file_sizes[i % len(self._file_sizes)]
- if fsize is None:
- raise Exception("file sizes?: {0} {1}".format(i, fsize))
- fname = "{0}-{1}k.txt".format(i, fsize)
- mk_text_file(os.path.join(docs_a, fname), 8 * fsize)
- uris.append(f"{self._location}{fname}")
- t.update()
- with open(self._url_file, 'w') as fd:
- fd.write("\n".join(uris))
- fd.write("\n")
- ConnectionLoadTest.SETUP_DONE = True
- self.start_server(env=self.env, cd=self._cd)
-
- def _teardown(self):
- pass
-
- def run_test(self, mode: str, path: str) -> H2LoadLogSummary:
- _mode = mode
- _path = path
- monitor = None
- try:
- log_file = "{gen_dir}/h2load.log".format(gen_dir=self.env.gen_dir)
- if os.path.isfile(log_file):
- os.remove(log_file)
- monitor = H2LoadMonitor(log_file, expected=0,
- title=f"{self._ssl_module}/{self._protocol}/"
- f"{self._clients}c/{self._duration.total_seconds()}s")
- monitor.start()
- args = [
- 'h2load',
- '--clients={0}'.format(self._clients),
- '--requests={0}'.format(self._requests * self._clients),
- '--input-file={0}'.format(self._url_file),
- '--log-file={0}'.format(log_file),
- '--connect-to=localhost:{0}'.format(self.env.https_port)
- ]
- if self._protocol == 'h1' or self._protocol == 'http/1.1':
- args.append('--h1')
- elif self._protocol == 'h2':
- args.extend(['-m', "6"])
- else:
- raise Exception(f"unknown protocol: {self._protocol}")
- args += [
- f'--base-uri=https://{self.env.domain_a}:{self.env.https_port}{self._location}'
- ]
- end = datetime.now() + self._duration
- r = None
- while datetime.now() < end:
- r = self.env.run(args)
- if r.exit_code != 0:
- raise LoadTestException("h2load returned {0}: {1}".format(r.exit_code, r.stderr))
- summary = monitor.get_summary(duration=self._duration)
- summary.set_exec_result(r)
- return summary
- finally:
- if monitor is not None:
- monitor.stop()
-
- def run(self) -> H2LoadLogSummary:
- path = self._setup()
- try:
- return self.run_test(mode="measure", path=path)
- finally:
- self._teardown()
-
- def format_result(self, summary: H2LoadLogSummary) -> Tuple[str, Optional[List[str]]]:
- return "{0:.0f}".format(
- summary.response_count / summary.duration.total_seconds() / self._requests
- ), summary.get_footnote()
-
-
-class LoadTest:
-
- @staticmethod
- def print_table(table: List[List[str]], foot_notes: List[str] = None):
- col_widths = []
- col_sep = " "
- for row in table[1:]:
- for idx, cell in enumerate(row):
- if idx >= len(col_widths):
- col_widths.append(len(cell))
- else:
- col_widths[idx] = max(len(cell), col_widths[idx])
- row_len = sum(col_widths) + (len(col_widths) * len(col_sep))
- print(f"{' '.join(table[0]):^{row_len}}")
- for row in table[1:]:
- line = ""
- for idx, cell in enumerate(row):
- line += f"{col_sep if idx > 0 else ''}{cell:>{col_widths[idx]}}"
- print(line)
- if foot_notes is not None:
- for idx, note in enumerate(foot_notes):
- print("{0:3d}) {1}".format(idx + 1, note))
-
- @staticmethod
- def scenario_with(base: Dict, updates: Dict) -> Dict:
- scenario = base.copy()
- scenario.update(updates)
- return scenario
-
- @classmethod
- def main(cls):
- parser = argparse.ArgumentParser(prog='load_h1', description="""
- Run a range of load tests against the test Apache setup.
- """)
- parser.add_argument("-m", "--module", type=str, default=None,
- help="which module to test, defaults to all")
- parser.add_argument("-p", "--protocol", type=str, default=None,
- help="which protocols to test, defaults to all")
- parser.add_argument("-v", "--verbose", action='count', default=0,
- help="log more output on stderr")
- parser.add_argument("names", nargs='*', help="Name(s) of scenarios to run")
- args = parser.parse_args()
-
- if args.verbose > 0:
- console = logging.StreamHandler()
- console.setLevel(logging.INFO)
- console.setFormatter(logging.Formatter(logging.BASIC_FORMAT))
- logging.getLogger('').addHandler(console)
-
- rv = 0
- env = TlsTestEnv()
-
- try:
- log.debug("starting tests")
-
- scenario_sf = {
- "title": "sizes and throughput (MB/s)",
- "class": SingleFileLoadTest,
- "location": "/",
- "clients": 0,
- "row0_title": "module protocol",
- "row_title": "{module} {protocol}",
- "rows": [
- {"module": "mod_ssl", "protocol": 'h1'},
- {"module": "mod_tls", "protocol": 'h1'},
- {"module": "mod_ssl", "protocol": 'h2'},
- {"module": "mod_tls", "protocol": 'h2'},
- ],
- "col_title": "{rsize}KB",
- "columns": [],
- }
- scenario_mf = {
- "title": "connections and throughput (MB/s)",
- "class": MultiFileLoadTest,
- "location": "/",
- "file_count": 1024,
- "file_sizes": [1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100, 10000],
- "requests": 10000,
- "row0_title": "module protocol",
- "row_title": "{module} {protocol}",
- "rows": [
- {"module": "mod_ssl", "protocol": 'h1'},
- {"module": "mod_tls", "protocol": 'h1'},
- {"module": "mod_ssl", "protocol": 'h2'},
- {"module": "mod_tls", "protocol": 'h2'},
- ],
- "col_title": "{clients}c",
- "columns": [],
- }
- scenario_conn = {
- "title": "connections",
- "class": ConnectionLoadTest,
- "location": "/",
- "duration": timedelta(seconds=10),
- "cooldown": timedelta(seconds=5),
- "file_count": 12,
- "file_sizes": [1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100, 10000],
- "requests": 1,
- "clients": 1,
- "row0_title": "module protocol",
- "row_title": "{module} {protocol}",
- "rows": [
- {"module": "mod_ssl", "protocol": 'h1'},
- {"module": "mod_tls", "protocol": 'h1'},
- {"module": "mod_ssl", "protocol": 'h2'},
- {"module": "mod_tls", "protocol": 'h2'},
- ],
- "col_title": "{clients}c",
- "columns": [],
- }
-
- scenarios = {
- "1c-throughput": cls.scenario_with(scenario_sf, {
- "title": "1 conn, 1k-10k requests, *sizes, throughput (MB/s)",
- "clients": 1,
- "columns": [
- {"requests": 10000, "rsize": 10},
- {"requests": 6000, "rsize": 100},
- {"requests": 3000, "rsize": 1024},
- {"requests": 1000, "rsize": 10 * 1024},
- ],
- }),
- "10c-throughput": cls.scenario_with(scenario_sf, {
- "title": "10 conn, 5k-50k requests, *sizes, throughput (MB/s)",
- "clients": 10,
- "columns": [
- {"requests": 50000, "rsize": 10},
- {"requests": 25000, "rsize": 100},
- {"requests": 10000, "rsize": 1024},
- {"requests": 5000, "rsize": 10 * 1024},
- ],
- }),
- "20c-throughput": cls.scenario_with(scenario_sf, {
- "title": "20 conn, 5k-50k requests, *sizes, throughput (MB/s)",
- "clients": 20,
- "columns": [
- {"requests": 50000, "rsize": 10},
- {"requests": 25000, "rsize": 100},
- {"requests": 10000, "rsize": 1024},
- {"requests": 5000, "rsize": 10 * 1024},
- ],
- }),
- "50c-throughput": cls.scenario_with(scenario_sf, {
- "title": "50 conn, 10k-100k requests, *sizes, throughput (MB/s)",
- "clients": 50,
- "columns": [
- {"requests": 100000, "rsize": 10},
- {"requests": 50000, "rsize": 100},
- {"requests": 10000, "rsize": 1024},
- {"requests": 5000, "rsize": 10 * 1024},
- ],
- }),
- "1k-files": cls.scenario_with(scenario_mf, {
- "title": "1k files, 1k-10MB, *conn, 10k req, (req/s)",
- "clients": 1,
- "columns": [
- {"clients": 1},
- {"clients": 2},
- {"clients": 4},
- {"clients": 8},
- {"clients": 16},
- {"clients": 32},
- {"clients": 64},
- ],
- }),
- "1k-files-proxy-h1": cls.scenario_with(scenario_mf, {
- "location": "/proxy-h1/",
- "title": "1k files, h1 proxy, 1k-10MB, *conn, 10k req, (req/s)",
- "clients": 1,
- "columns": [
- {"clients": 1},
- {"clients": 2},
- {"clients": 4},
- {"clients": 8},
- {"clients": 16},
- {"clients": 32},
- {"clients": 64},
- ],
- }),
- "1k-files-proxy-h2": cls.scenario_with(scenario_mf, {
- "location": "/proxy-h2/",
- "title": "1k files, h2 proxy, 1k-10MB, *conn, 10k req, (req/s)",
- "clients": 1,
- "columns": [
- {"clients": 1},
- {"clients": 2},
- {"clients": 4},
- {"clients": 8},
- {"clients": 16},
- {"clients": 32},
- {"clients": 64},
- ],
- }),
- "1m-reqs": cls.scenario_with(scenario_mf, {
- "title": "1m requests, 1k files, 1k-10MB, (req/s)",
- "clients": 1,
- "requests": 1000000,
- "columns": [
- {"clients": 1},
- {"clients": 4},
- {"clients": 16},
- {"clients": 64},
- ],
- }),
- "conn-scale": cls.scenario_with(scenario_conn, {
- "title": "c parallel clients, 1 req/c (conn/s)",
- "requests": 1,
- "duration": timedelta(seconds=30),
- "cooldown": timedelta(seconds=10),
- "columns": [
- {"clients": 1},
- {"clients": 2},
- {"clients": 4},
- {"clients": 8},
- {"clients": 16},
- {"clients": 32},
- ],
- }),
- "conn-limits": cls.scenario_with(scenario_conn, {
- "title": "c parallel clients, 1 req/c (conn/s)",
- "requests": 1,
- "duration": timedelta(seconds=10),
- "cooldown": timedelta(seconds=30),
- "columns": [
- {"clients": 64},
- {"clients": 128},
- {"clients": 256},
- {"clients": 512},
- ],
- }),
- }
- for name in args.names:
- if name not in scenarios:
- raise LoadTestException(f"scenario unknown: '{name}'")
- names = args.names if len(args.names) else sorted(scenarios.keys())
-
- setup = TlsTestSetup(env=env)
- env.setup_httpd(setup=setup)
-
- for name in names:
- scenario = scenarios[name]
- table = [
- [scenario['title']],
- ]
- foot_notes = []
- headers = [scenario['row0_title']]
- for col in scenario['columns']:
- headers.append(scenario['col_title'].format(**col))
- table.append(headers)
- cls.print_table(table)
- for row in scenario['rows']:
- if args.module is not None and row['module'] != args.module:
- continue
- if args.protocol is not None and row['protocol'] != args.protocol:
- continue
- row_line = [scenario['row_title'].format(**row)]
- table.append(row_line)
- for col in scenario['columns']:
- t = scenario.copy()
- t.update(row)
- t.update(col)
- test = scenario['class'].from_scenario(t, env=env)
- env.httpd_error_log.clear_log()
- summary = test.run()
- result, fnote = test.format_result(summary)
- if fnote:
- foot_notes.append(fnote)
- row_line.append("{0}{1}".format(result,
- f"[{len(foot_notes)}]" if fnote else ""))
- cls.print_table(table, foot_notes)
- except KeyboardInterrupt:
- rv = 1
- except LoadTestException as ex:
- sys.stderr.write(f"ERROR: {str(ex)}\n")
- rv = 1
-
- env.apache_stop()
- sys.exit(rv)
-
-
-if __name__ == "__main__":
- LoadTest.main()