# Metadata lives in ../latency-budget.json. This notebook uses synthetic data.
import marimo

__generated_with = "0.16.5"
app = marimo.App(width="medium")

@app.cell
def _():
    import marimo as mo
    return (mo,)

@app.cell
def _(mo):
    mo.md("""
    ## The average is not the experience
    Consider a synthetic workload: 95 requests take 40 ms each, and 5 take 800 ms.
    The mean weights every request equally; a percentile identifies a position
    in the sorted distribution.
    """)
    return

@app.cell
def _():
    import math
    latencies = [40] * 95 + [800] * 5
    def percentile(values, p):
        ordered = sorted(values)
        return ordered[max(0, math.ceil(p * len(ordered)) - 1)]
    mean = sum(latencies) / len(latencies)
    p99 = percentile(latencies, 0.99)
    return latencies, mean, p99

@app.cell
def _(mo, mean, p99):
    mo.md(f"Mean: **{mean:.0f} ms**. P99: **{p99:.0f} ms**. The mean hides a slow path that one in twenty requests takes.")
    return

@app.cell
def _(mo):
    mo.md("""
    ## Budget for the slow path
    A service that calls several dependencies can inherit the tail of each one.
    Deadlines, bounded retries, and queue limits should form one coherent budget.

    ## Choose the measurement window
    This calculation uses the nearest-rank percentile: sort the observations and
    select rank ceil(p × n). With 100 requests, p99 is the 99th observation.
    Track percentiles alongside request volume and the measurement window;
    changing either can change how much evidence supports the reported tail.
    """)
    return

if __name__ == "__main__":
    app.run()
