"""Sample container CPU and memory during a sizing run. WHY: the sizing table claims specific vCPU and RAM figures. Those can only be confirmed by watching the container under load, not by reasoning about greenlet size. This records peak RSS and CPU so a claimed "4 GB is enough" is either demonstrated or falsified. Runs on the HOST (needs the docker CLI), unlike the generators. Usage: python sample_resources.py --container shopdb-sizing-api --seconds 120 """ import argparse import json import re import statistics import subprocess import time def parse_size(text): """Docker stats prints like '1.234GiB' / '512MiB'. Return megabytes.""" match = re.match(r'^([0-9.]+)\s*([KMGT]?i?B)$', text.strip()) if not match: return 0.0 value = float(match.group(1)) unit = match.group(2).upper().replace('I', '') factors = {'B': 1.0 / (1024 * 1024), 'KB': 1.0 / 1024, 'MB': 1.0, 'GB': 1024.0, 'TB': 1024.0 * 1024.0} return value * factors.get(unit, 1.0) def main(): parser = argparse.ArgumentParser() parser.add_argument('--container', required=True) parser.add_argument('--seconds', type=int, default=120) parser.add_argument('--interval', type=float, default=2.0) arguments = parser.parse_args() cpu_samples = [] memory_samples = [] deadline = time.monotonic() + arguments.seconds while time.monotonic() < deadline: try: output = subprocess.check_output( ['docker', 'stats', '--no-stream', '--format', '{{.CPUPerc}}|{{.MemUsage}}', arguments.container], stderr=subprocess.DEVNULL, text=True).strip() except subprocess.CalledProcessError: break if not output: break cpu_text, memory_text = output.split('|', 1) cpu_samples.append(float(cpu_text.replace('%', '').strip())) used = memory_text.split('/')[0] memory_samples.append(parse_size(used)) time.sleep(arguments.interval) def summarize(values): if not values: return {'samples': 0} ordered = sorted(values) index = min(int(len(ordered) * 0.95), len(ordered) - 1) return { 'samples': len(values), 'median': round(statistics.median(values), 1), 'p95': round(ordered[index], 1), 'max': round(max(values), 1), } print(json.dumps({ 'container': arguments.container, 'cpu_percent': summarize(cpu_samples), 'memory_mb': summarize(memory_samples), }, indent=2)) if __name__ == '__main__': main()