Files
apache-http-server/test/modules/core/test_002_restarts.py
Stefan Eissing 0e9d2a6c8a *) test: core stress test_core_002 enhanved to monitor dynamic child
changes on load and graceful reload of the server.



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1899885 13f79535-47bb-0310-9956-ffa450edef68
2022-04-15 10:59:22 +00:00

151 lines
5.6 KiB
Python

import os
import re
import time
from datetime import datetime, timedelta
from threading import Thread
import pytest
from .env import CoreTestEnv
from pyhttpd.conf import HttpdConf
class Loader:
def __init__(self, env, url: str, clients: int, req_per_client: int = 10):
self.env = env
self.url = url
self.clients = clients
self.req_per_client = req_per_client
self.result = None
self.total_request = 0
self._thread = None
def run(self):
self.total_requests = self.clients * self.req_per_client
conn_per_client = 5
args = [self.env.h2load, f"--connect-to=localhost:{self.env.https_port}",
"--h1", # use only http/1.1
"-n", str(self.total_requests), # total # of requests to make
"-c", str(conn_per_client * self.clients), # total # of connections to make
"-r", str(self.clients), # connections at a time
"--rate-period", "2", # create conns every 2 sec
self.url,
]
self.result = self.env.run(args)
def start(self):
self._thread = Thread(target=self.run)
self._thread.start()
def join(self):
self._thread.join()
class ChildDynamics:
RE_DATE_TIME = re.compile(r'\[(?P<date_time>[^\]]+)\] .*')
RE_TIME_FRAC = re.compile(r'(?P<dt>.* \d\d:\d\d:\d\d)(?P<frac>.(?P<micros>.\d+)) (?P<year>\d+)')
RE_CHILD_CHANGE = re.compile(r'\[(?P<date_time>[^\]]+)\] '
r'\[mpm_event:\w+\]'
r' \[pid (?P<main_pid>\d+):tid \w+\] '
r'.* Child (?P<child_no>\d+) (?P<action>\w+): '
r'pid (?P<pid>\d+), gen (?P<generation>\d+), .*')
def __init__(self, env: CoreTestEnv):
self.env = env
self.changes = list()
self._start = None
for l in open(env.httpd_error_log.path):
m = self.RE_CHILD_CHANGE.match(l)
if m:
self.changes.append({
'pid': int(m.group('pid')),
'child_no': int(m.group('child_no')),
'gen': int(m.group('generation')),
'action': m.group('action'),
'rtime' : self._rtime(m.group('date_time'))
})
continue
if self._start is None:
m = self.RE_DATE_TIME.match(l)
if m:
self._rtime(m.group('date_time'))
def _rtime(self, s: str) -> timedelta:
micros = 0
m = self.RE_TIME_FRAC.match(s)
if m:
micros = int(m.group('micros'))
s = f"{m.group('dt')} {m.group('year')}"
d = datetime.strptime(s, '%a %b %d %H:%M:%S %Y') + timedelta(microseconds=micros)
if self._start is None:
self._start = d
delta = d - self._start
return f"{delta.seconds:+02d}.{delta.microseconds:06d}"
@pytest.mark.skipif(condition='STRESS_TEST' not in os.environ,
reason="STRESS_TEST not set in env")
@pytest.mark.skipif(condition=not CoreTestEnv().h2load_is_at_least('1.41.0'),
reason="h2load unavailable or misses --connect-to option")
class TestRestarts:
def test_core_002_01(self, env):
# Lets make a tight config that triggers dynamic child behaviour
conf = HttpdConf(env, extras={
'base': f"""
StartServers 1
ServerLimit 3
ThreadLimit 4
ThreadsPerChild 4
MinSpareThreads 4
MaxSpareThreads 6
MaxRequestWorkers 12
MaxConnectionsPerChild 0
LogLevel mpm_event:trace6
""",
})
conf.add_vhost_cgi()
conf.install()
# clear logs and start server, start load
env.httpd_error_log.clear_log()
assert env.apache_restart() == 0
# we should see a single child started
cd = ChildDynamics(env)
assert len(cd.changes) == 1, f"{cd.changes}"
assert cd.changes[0]['action'] == 'started'
# This loader simulates 6 clients, each making 10 requests.
# delay.py sleeps for 1sec, so this should run for about 10 seconds
loader = Loader(env=env, url=env.mkurl("https", "cgi", "/delay.py"),
clients=6, req_per_client=10)
loader.start()
# Expect 2 more children to have been started after half time
time.sleep(5)
cd = ChildDynamics(env)
assert len(cd.changes) == 3, f"{cd.changes}"
assert len([x for x in cd.changes if x['action'] == 'started']) == 3, f"{cd.changes}"
# Trigger a server reload
assert env.apache_reload() == 0
# a graceful reload lets ongoing requests continue, but
# after a while all gen 0 children should have stopped
time.sleep(3) # FIXME: this pbly depends on the runtime a lot, do we have expectations?
cd = ChildDynamics(env)
gen0 = [x for x in cd.changes if x['gen'] == 0]
assert len([x for x in gen0 if x['action'] == 'stopped']) == 3
# wait for the loader to finish and stop the server
loader.join()
env.apache_stop()
# Similar to before the reload, we expect 3 children to have
# been started and stopped again on server stop
cd = ChildDynamics(env)
gen1 = [x for x in cd.changes if x['gen'] == 1]
assert len([x for x in gen1 if x['action'] == 'started']) == 3
assert len([x for x in gen1 if x['action'] == 'stopped']) == 3