diff options
Diffstat (limited to 'research/experiments/030-app-lifecycle/isolation.py')
| -rw-r--r-- | research/experiments/030-app-lifecycle/isolation.py | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/research/experiments/030-app-lifecycle/isolation.py b/research/experiments/030-app-lifecycle/isolation.py new file mode 100644 index 0000000..43259f4 --- /dev/null +++ b/research/experiments/030-app-lifecycle/isolation.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +"""Another app's latency while one app changes again and again on a running +host (docs/design/017's stage 3, research 115). + +A host of apps (two connection threads, two workers, stores in RAM, cores +0-2) holds A, an app of items.aup with ITEMS items, and B, the scoreboard +(benchmarks/http/scores.aup). B takes one connection's scores +(benchmarks/http/load on core 3) for SECONDS, while A is left be (quiet) +or changes every EVERY seconds (changing: SIGHUP, its line naming in turn +items.pmod and NEXT). The two take turns, their order turned each round. + +isolation.py PURE_SERVE ITEMS NEXT EVERY SECONDS ROUNDS a JSON line per run, + then one with medians +""" +import json +import select +import shutil +import signal +import statistics +import subprocess +import sys +import tempfile +import threading +import time +from pathlib import Path + +from window import AUP, HERE, grow + +ROOT = HERE.parents[2] +LOAD = ROOT / 'benchmarks' / 'http' / 'load' + + +def main(): + serve, items, after = Path(sys.argv[1]).resolve(), int(sys.argv[2]), sys.argv[3] + every, seconds, rounds = float(sys.argv[4]), int(sys.argv[5]), int(sys.argv[6]) + work = Path(tempfile.mkdtemp(prefix='pure-030-', dir='/dev/shm')) + try: + modules = {} + for name, source in (('items', HERE / 'items.aup'), (after, HERE / f'{after}.aup'), + ('scores', ROOT / 'benchmarks' / 'http' / 'scores.aup')): + modules[name] = work / f'{name}.pmod' + subprocess.run([str(AUP), 'build', str(source), '-o', str(modules[name])], check=True) + conf = work / 'apps.conf' + + def write(module): + conf.write_text(f"a {modules[module]} {work / 'a'}\nb {modules['scores']} {work / 'b'}\n") + + write('items') + host = subprocess.Popen(['taskset', '-c', '0-2', str(serve), '--apps', str(conf), '--port', '0', '--threads', '2', + '--workers', '2'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + port = json.loads(host.stdout.readline())['port'] + count = grow(port, items, 'a') + p99 = {'quiet': [], 'changing': []} + changes = 0 + for round_ in range(1, rounds + 1): + for way in (['quiet', 'changing'] if round_ % 2 else ['changing', 'quiet']): + load = subprocess.Popen(['taskset', '-c', '3', str(LOAD), str(port), '1', str(seconds), 'score', '1', + 'keep-alive', '16', '/b'], stdout=subprocess.PIPE, text=True) + made = 0 + while way == 'changing' and load.poll() is None: + write(after if changes % 2 == 0 else 'items') + host.send_signal(signal.SIGHUP) + ready, _, _ = select.select([host.stdout], [], [], 60) + assert ready + said = json.loads(host.stdout.readline()) + assert said['apps'][0]['running'], said + changes += 1 + made += 1 + time.sleep(every) + out, _ = load.communicate() + result = json.loads(out) + assert result['5xx'] == 0 and result['errors'] == 0, result + p99[way].append(result['p99_us']) + print(json.dumps({'round': round_, 'way': way, 'items': count, 'next': after, 'changes': made, **result}), + flush=True) + host.terminate() + host.communicate(timeout=600) + quiet, changing = statistics.median(p99['quiet']), statistics.median(p99['changing']) + print(json.dumps({'items': count, 'next': after, 'every_s': every, 'quiet_p99_us': quiet, + 'changing_p99_us': changing, 'ratio': round(changing / quiet, 2)}), flush=True) + finally: + shutil.rmtree(work) + + +if __name__ == '__main__': + main() |
