Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a15374-f59f-7a31-86d2-d23b5d2ed181
69 lines
3 KiB
Python
69 lines
3 KiB
Python
import argparse
|
|
import json
|
|
import math
|
|
from pathlib import Path
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description="Compare measured application cgroup limits; never apply resources"
|
|
)
|
|
parser.add_argument("input", type=Path)
|
|
parser.add_argument("--output", type=Path, required=True)
|
|
parser.add_argument("--expected-cpu-m", type=int, default=1000)
|
|
parser.add_argument("--expected-memory-mib", type=int, default=1024)
|
|
args = parser.parse_args()
|
|
v = json.loads(args.input.read_text())
|
|
if not v.get("measurement_complete") or v.get("measurement_gaps"):
|
|
raise ValueError("measurement gaps prevent a limit comparison")
|
|
if v["app_runtime"]["nano_cpus"] != args.expected_cpu_m * 1000000:
|
|
raise ValueError("actual CPU limit does not match expected limit")
|
|
if v["app_runtime"]["memory_bytes"] != args.expected_memory_mib * 1024**2:
|
|
raise ValueError("actual memory limit does not match expected limit")
|
|
samples = [s for s in v["runtime_samples"] if s.get("phase") == "ordinary" and "cpu_seconds" in s]
|
|
rates = [
|
|
(b["cpu_seconds"] - a["cpu_seconds"]) / (b["monotonic_seconds"] - a["monotonic_seconds"]) * 1000
|
|
for a, b in zip(samples, samples[1:])
|
|
]
|
|
|
|
|
|
def p95(values):
|
|
return sorted(values)[math.ceil(0.95 * len(values)) - 1]
|
|
|
|
|
|
start, end = samples[0]["cpu_stat"], samples[-1]["cpu_stat"]
|
|
db = [s["docker_stats"] for s in v["database_samples"] if s["phase"] == "ordinary"]
|
|
report = {
|
|
"status": "provisional-existing-limit-comparison",
|
|
"ordinary": v["ordinary"],
|
|
"document_transfer": v["document_transfer"],
|
|
"complete": v["complete"],
|
|
"provisional_pass": v["provisional_pass"],
|
|
"measurement_complete": v["measurement_complete"],
|
|
"measurement_gaps": v["measurement_gaps"],
|
|
"config": v["config"],
|
|
"completed_iterations": v["completed_iterations"],
|
|
"app_runtime": v["app_runtime"],
|
|
"application_measured": {
|
|
"sample_intervals": len(rates),
|
|
"cpu_p95_m": p95(rates),
|
|
"cpu_mean_m": (samples[-1]["cpu_seconds"] - samples[0]["cpu_seconds"])
|
|
/ (samples[-1]["monotonic_seconds"] - samples[0]["monotonic_seconds"])
|
|
* 1000,
|
|
"cgroup_memory_peak_mib": max(s["rss_bytes"] for s in samples) / 1024**2,
|
|
"throttled_periods": end["nr_throttled"] - start["nr_throttled"],
|
|
"periods": end["nr_periods"] - start["nr_periods"],
|
|
"throttled_usec": end["throttled_usec"] - start["throttled_usec"],
|
|
},
|
|
"database_separate": {
|
|
"samples": len(db),
|
|
"cpu_p95_percent": p95([float(s["CPUPerc"].rstrip("%")) for s in db]),
|
|
"memory_snapshots_kind": "docker usage; full intervals retained raw",
|
|
},
|
|
"interpretation": (
|
|
"No production limit increase implied. The inspected limit is tested only for this "
|
|
"local synthetic mix. 60m request is not validated as adequate under contention. "
|
|
"Memory includes page cache; comparison to earlier summedRSS is not equivalent. "
|
|
"No human/ingress/SSO acceptance."
|
|
),
|
|
}
|
|
args.output.write_text(json.dumps(report, indent=2) + "\n")
|
|
print(json.dumps(report, indent=2))
|