#!/usr/bin/env python3 """catalog_check.py — §7 C: measured memory against each template's mem_request and mem_limit. Writes ../evidence/catalog-check.json and prints two lists: RISK the app's peak > 80 % of its mem_limit, OR any one container's bench peak > 80 % of that container's own compose limit (the limit Docker enforces is per container) OVER the app's typical use > its mem_request (the catalog under-states what it needs) Read only: nothing in the catalog is changed. """ import json, os HERE = os.path.dirname(os.path.abspath(__file__)) EV = os.path.join(os.path.dirname(HERE), "evidence") t = json.load(open(os.path.join(EV, "apps-table.json"))) risk, over = [], [] for a in sorted(t, key=lambda a: a["slug"]): if a["source"] == "unmeasured": continue lim, req = a["catalog_limit_mb"], a["catalog_request_mb"] why = [] if lim and a["mem_peak_mb"] and a["mem_peak_mb"] > 0.8 * lim: why.append(f"app peak {a['mem_peak_mb']} MB = {100 * a['mem_peak_mb'] / lim:.0f} % of mem_limit {lim} MB") for cname, c in ((a.get("bench") or {}).get("containers") or {}).items(): if c.get("limit_mb") and c["peak_used_mb"] > 0.8 * c["limit_mb"]: why.append(f"container {cname} peak {c['peak_used_mb']} MB = {100 * c['peak_used_mb'] / c['limit_mb']:.0f} % of its {c['limit_mb']} MB limit") if why: risk.append(dict(slug=a["slug"], why=why, source=a["source"])) if req and a["mem_typical_mb"] and a["mem_typical_mb"] > req: over.append(dict(slug=a["slug"], typical_mb=a["mem_typical_mb"], mem_request_mb=req, ratio=round(a["mem_typical_mb"] / req, 1), source=a["source"])) json.dump(dict(risk=risk, over_request=over), open(os.path.join(EV, "catalog-check.json"), "w"), indent=1) print("RISK (peak > 80 % of a limit):", len(risk)) for r in risk: print(f" {r['slug']:16} {r['source']:12} " + "; ".join(r["why"])) print("OVER (typical > mem_request):", len(over)) for o in sorted(over, key=lambda o: -o["ratio"]): print(f" {o['slug']:16} typical {o['typical_mb']:>5} > request {o['mem_request_mb']:>5} x{o['ratio']} ({o['source']})")