aboutsummaryrefslogtreecommitdiffstats
path: root/research/experiments/030-app-lifecycle/isolation.py
diff options
context:
space:
mode:
Diffstat (limited to 'research/experiments/030-app-lifecycle/isolation.py')
-rw-r--r--research/experiments/030-app-lifecycle/isolation.py86
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()