Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a15374-f59f-7a31-86d2-d23b5d2ed181
84 lines
3.7 KiB
Python
84 lines
3.7 KiB
Python
"""Derive a provisional local-host envelope, never apply deployment resources."""
|
|
|
|
import argparse
|
|
import json
|
|
import math
|
|
from pathlib import Path
|
|
|
|
|
|
def percentile(values):
|
|
values = sorted(values)
|
|
if not values:
|
|
raise ValueError("measurement samples missing")
|
|
return values[math.ceil(0.95 * len(values)) - 1]
|
|
|
|
|
|
def recommend(result, margin):
|
|
if result.get("measurement_gaps") or result.get("measurement_complete") is False:
|
|
raise ValueError("sampling gaps prevent a sizing recommendation")
|
|
if result.get("app_runtime"):
|
|
raise ValueError(
|
|
"quota-capped run requires direct limit comparison, not unbounded extrapolation"
|
|
)
|
|
samples = result["runtime_samples"]
|
|
cpu = []
|
|
memory = []
|
|
for before, after in zip(samples, samples[1:]):
|
|
if before.get("phase") != "ordinary" or after.get("phase") != "ordinary":
|
|
continue
|
|
if before.get("pids") != after.get("pids"):
|
|
continue
|
|
elapsed = after["monotonic_seconds"] - before["monotonic_seconds"]
|
|
used = after["cpu_seconds"] - before["cpu_seconds"]
|
|
if elapsed > 0 and used >= 0:
|
|
cpu.append(used / elapsed * 1000)
|
|
memory.append(after["rss_bytes"] / 1024**2)
|
|
if not cpu or not memory or not result["complete"] or not result["provisional_pass"]:
|
|
raise ValueError("incomplete or failed workload cannot establish a sizing envelope")
|
|
cpu_p95, cpu_peak, memory_peak = percentile(cpu), max(cpu), max(memory)
|
|
|
|
def rounded(value, quantum):
|
|
return math.ceil(value / quantum) * quantum
|
|
|
|
return {
|
|
"status": "provisional-local-host-only",
|
|
"application_observed": {
|
|
"cpu_p95_m": cpu_p95,
|
|
"cpu_peak_m": cpu_peak,
|
|
"rss_sum_peak_mib": memory_peak,
|
|
"sample_intervals": len(cpu),
|
|
"normalized_cpu_p95_m_per_worker": cpu_p95 / result["config"]["workers"],
|
|
},
|
|
"suggested_application": {
|
|
"cpu_request_m": rounded(cpu_p95 * margin["cpu_request_multiplier"], 25),
|
|
"cpu_limit_m": rounded(cpu_peak * margin["cpu_limit_multiplier"], 100),
|
|
"memory_request_mib": rounded(memory_peak * margin["memory_request_multiplier"], 64),
|
|
"memory_limit_mib": rounded(memory_peak * margin["memory_limit_multiplier"], 64),
|
|
},
|
|
"margins": margin,
|
|
"limits": [
|
|
"Per-worker number is normalized aggregate, not an independently sampled worker.",
|
|
"RSS sum double-counts shared pages; conservative upper bound, not unique memory.",
|
|
"Observed local CPU is hardware/think-time dependent; quota effects untested.",
|
|
"Database/container demand is separate and not included in application request.",
|
|
"No production apply or human acceptance; validate target runtime before right-sizing.",
|
|
],
|
|
"revisit": "Changed concurrency, corpus, document size, workers, image, hardware or quota; "
|
|
"then repeat under approved target runtime including bursts and restart.",
|
|
}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("result", type=Path)
|
|
parser.add_argument("--margins", type=Path, required=True)
|
|
parser.add_argument("--output", type=Path, required=True)
|
|
args = parser.parse_args()
|
|
margins = json.loads(args.margins.read_text())
|
|
if any(
|
|
not isinstance(v, (int, float)) or not math.isfinite(v) or v < 1 for v in margins.values()
|
|
):
|
|
raise ValueError("all margins must be finite and at least one")
|
|
args.output.write_text(
|
|
json.dumps(recommend(json.loads(args.result.read_text()), margins), indent=2) + "\n"
|
|
)
|