"""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" )