# /// script
# requires-python = ">=3.10"
# ///
# Copyright (c) 2026 Aayush Sharma · SPDX-License-Identifier: MIT
# https://thedataengineering.com/lab-code/LICENSE.txt
"""Throughput against goodput past the knee.

A service completes 100 requests per second and receives 150; clients give up
after 2 seconds. (a) an unbounded queue, (b) a queue bounded at 200, (c) the
bounded queue plus a deadline check at dequeue. Goodput (answers delivered to
clients still waiting) per second over a minute.
"""
import argparse, json
from collections import deque

p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
p.add_argument("--arrival", type=int, default=150, help="Requests per second.")
p.add_argument("--capacity", type=int, default=100, help="Requests served per second.")
p.add_argument("--timeout", type=int, default=2, help="Client timeout (s).")
p.add_argument("--max-queue", type=int, default=200, help="Bound for the bounded queue.")
p.add_argument("--seconds", type=int, default=60, help="Seconds simulated.")
p.add_argument("--json", help="Also write the results to this JSON file.")
args = p.parse_args()

def sim(max_queue=None, check=False):
    q, good, per_sec = deque(), 0, []
    for sec in range(args.seconds):
        g0 = good
        for _ in range(args.arrival):
            if max_queue is None or len(q) < max_queue: q.append(sec)      # remember the arrival second
        for _ in range(args.capacity):
            while q:
                arr = q.popleft()
                if sec - arr > args.timeout:
                    if check: continue                                     # discard for free, try the next one
                    break                                                  # capacity spent on a dead request
                good += 1; break
        per_sec.append(good - g0)
    return good / args.seconds, per_sec
rows, timeline = [], []
for label, kw in (("unbounded", {}), (f"bounded ({args.max_queue})", {"max_queue": args.max_queue}),
                  (f"bounded ({args.max_queue}) + dequeue check", {"max_queue": args.max_queue, "check": True})):
    g, ps = sim(**kw); rows.append({"queue": label, "goodput": g, "last_10s": sum(ps[-10:]) / 10})
    timeline += [{"queue": label, "second": i, "goodput": v} for i, v in enumerate(ps)]
for r in rows: print(f"{r['queue']:<34} goodput {r['goodput']:6.1f}/s over the minute, {r['last_10s']:6.1f}/s in the last 10 s")

if args.json:
    with open(args.json, "w") as f:
        json.dump({"tables": {"queues": rows, "timeline": timeline}}, f)
