Spaces:
Running
Running
Download scripts/check_live_perf.py from Shurem/mz_chatbot: direct link, hf CLI and curl.
- Browser
- Download file 7.99 kB
-
https://huggingface.co/spaces/Shurem/mz_chatbot/resolve/main/scripts/check_live_perf.py
- Command line
-
hf download hf://spaces/Shurem/mz_chatbot/scripts/check_live_perf.py
-
curl -L -o check_live_perf.py https://huggingface.co/spaces/Shurem/mz_chatbot/resolve/main/scripts/check_live_perf.py
7.99 kB
| """ | |
| check_live_perf.py -- READ-ONLY check of the perf changes (slim list, gzip, per-lead detail, bell endpoints) | |
| against a backend you already have running -- your dev server on your real Neon data, or a deployed Space. | |
| uv run python scripts/check_live_perf.py # http://127.0.0.1:8000 | |
| uv run python scripts/check_live_perf.py --base-url https://<user>-<space>.hf.space | |
| uv run python scripts/check_live_perf.py --repeat 5 | |
| Only GET requests. Nothing is created, edited or deleted; the backend does the DB reads it always does. | |
| Auth: uses API_KEY from --api-key, the environment, or outreach-backend/.env (the server-to-server key the | |
| backend already accepts). If the backend has no API_KEY set and login is off, no key is needed. | |
| What it reports: | |
| * which DB endpoint the backend .env points at (pooled / direct -- host only, never the password) | |
| * GET /health | |
| * GET /leads (full) vs GET /leads?slim=1: rows, decoded bytes, bytes actually on the wire, Content-Encoding, | |
| and how long each took (first call, then the median of --repeat) | |
| * that slim rows really are empty on the six long-text fields, and everything else matches the full list | |
| * GET /leads/{id} for a lead that has a draft: full, slim=false, text present | |
| * GET /notifications/unread-count latency (the bell's only poll now) | |
| Exit code = number of FAILED checks. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import statistics | |
| import sys | |
| import time | |
| from pathlib import Path | |
| sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) | |
| import httpx # noqa: E402 | |
| from app.config import is_pooled_url, settings # noqa: E402 | |
| from app.schemas.lead import SLIM_DEFERRED # noqa: E402 | |
| FAILED = 0 | |
| def check(label: str, ok: bool, detail: str = "") -> None: | |
| global FAILED | |
| FAILED += 0 if ok else 1 | |
| print(f" [{'ok ' if ok else 'FAIL'}] {label}" + ("" if ok else f" {detail}")) | |
| def fetch(client: httpx.Client, path: str, params: dict | None = None) -> dict: | |
| """One GET asking for gzip; returns timing, wire bytes, decoded bytes, encoding and parsed JSON.""" | |
| t0 = time.perf_counter() | |
| with client.stream("GET", path, params=params) as r: | |
| wire = b"".join(r.iter_raw()) | |
| status, enc = r.status_code, r.headers.get("content-encoding") or "none" | |
| ms = (time.perf_counter() - t0) * 1000 | |
| body = wire | |
| if enc == "gzip": | |
| import gzip | |
| body = gzip.decompress(wire) | |
| data = None | |
| if status == 200: | |
| try: | |
| data = json.loads(body) | |
| except ValueError: | |
| data = None | |
| return {"status": status, "ms": ms, "wire": len(wire), "decoded": len(body), "enc": enc, "data": data} | |
| def timed(client: httpx.Client, path: str, params: dict | None, repeat: int) -> tuple[dict, float]: | |
| first = fetch(client, path, params) | |
| times = [fetch(client, path, params)["ms"] for _ in range(max(0, repeat - 1))] | |
| return first, (statistics.median(times) if times else first["ms"]) | |
| def kb(n: int) -> str: | |
| return f"{n / 1024:,.0f} KB" | |
| def main() -> int: | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--base-url", default="http://127.0.0.1:8000") | |
| ap.add_argument("--api-key", default="") | |
| ap.add_argument("--repeat", type=int, default=3, help="calls per endpoint after the first (median reported)") | |
| args = ap.parse_args() | |
| url = settings.sqlalchemy_url | |
| host = url.split("@")[-1].split("/")[0] if "@" in url else "(sqlite / not set)" | |
| print(f"\nBackend .env database: {host} -> {'POOLED (PgBouncer)' if is_pooled_url(url) else 'direct'}") | |
| print(f"Testing: {args.base_url}") | |
| key = args.api_key or settings.api_key | |
| headers = {"Accept-Encoding": "gzip"} | |
| if key: | |
| headers["X-API-Key"] = key | |
| client = httpx.Client(base_url=args.base_url.rstrip("/"), headers=headers, timeout=60) | |
| print("\nHealth") | |
| try: | |
| h = fetch(client, "/health") | |
| except httpx.HTTPError as exc: | |
| print(f" [FAIL] cannot reach {args.base_url}: {type(exc).__name__} -- is the backend running?") | |
| return 1 | |
| check("GET /health ok", h["status"] == 200 and (h["data"] or {}).get("db") == "ok", str(h["data"])) | |
| print("\nLead list: full vs slim") | |
| full, full_med = timed(client, "/leads", None, args.repeat) | |
| slim, slim_med = timed(client, "/leads", {"slim": 1}, args.repeat) | |
| if full["status"] != 200 or slim["status"] != 200: | |
| check("GET /leads both 200", False, f"full {full['status']}, slim {slim['status']} (401 = API key missing/wrong)") | |
| return FAILED | |
| fr, sr = full["data"], slim["data"] | |
| print(f" {len(fr)} leads") | |
| print(f" {'':10} {'decoded':>10} {'on the wire':>12} {'encoding':>9} {'first':>9} {'median':>9}") | |
| print(f" {'full':10} {kb(full['decoded']):>10} {kb(full['wire']):>12} {full['enc']:>9} {full['ms']:>7.0f}ms {full_med:>7.0f}ms") | |
| print(f" {'slim':10} {kb(slim['decoded']):>10} {kb(slim['wire']):>12} {slim['enc']:>9} {slim['ms']:>7.0f}ms {slim_med:>7.0f}ms") | |
| if full["wire"]: | |
| print(f" -> slim on the wire is {slim['wire'] / full['wire'] * 100:.0f}% of full on the wire; " | |
| f"gzip alone shrinks full by {(1 - full['wire'] / full['decoded']) * 100:.0f}%") | |
| check("same number of rows", len(fr) == len(sr), f"{len(fr)} vs {len(sr)}") | |
| check("gzip on the wire (Content-Encoding: gzip)", full["enc"] == "gzip" and slim["enc"] == "gzip", | |
| f"full={full['enc']} slim={slim['enc']} -- gzip middleware not deployed, or a proxy is stripping it") | |
| check("slim rows all say slim=true", all(x.get("slim") is True for x in sr)) | |
| check("full rows say slim=false", all(x.get("slim") is False for x in fr)) | |
| leaked = [f for x in sr for f in SLIM_DEFERRED if x.get(f) not in ("", [], None)] | |
| check("slim rows are empty on the six long-text fields", not leaked, f"{len(leaked)} non-empty values") | |
| light = [k for k in (fr[0] if fr else {}) if k not in SLIM_DEFERRED and k != "slim"] | |
| by_id = {x["id"]: x for x in sr} | |
| diff = [x["id"] for x in fr if x["id"] in by_id and any(x.get(k) != by_id[x["id"]].get(k) for k in light)] | |
| # A lead touched between the two calls can differ legitimately; more than a couple means a real mismatch. | |
| check("every other field matches the full list", len(diff) <= 2, f"{len(diff)} leads differ, e.g. ids {diff[:5]}") | |
| has_text = {f: sum(1 for x in fr if x.get(f)) for f in SLIM_DEFERRED} | |
| print(f" full list has text in: " + ", ".join(f"{f} {n}" for f, n in has_text.items())) | |
| print("\nSingle lead (what a card fetches when opened)") | |
| pick = next((x for x in fr if x.get("body") or x.get("phone_script")), fr[0] if fr else None) | |
| if pick is None: | |
| print(" (no leads at all -- skipped)") | |
| else: | |
| one, one_med = timed(client, f"/leads/{pick['id']}", None, args.repeat) | |
| d = one["data"] or {} | |
| print(f" GET /leads/{pick['id']}: {one['status']}, {kb(one['wire'])} on the wire, first {one['ms']:.0f}ms, median {one_med:.0f}ms") | |
| check("200", one["status"] == 200, str(one["status"])) | |
| check("full (slim=false)", d.get("slim") is False) | |
| check("long text present and equal to the full list's", | |
| all(d.get(f) == pick.get(f) for f in SLIM_DEFERRED)) | |
| check("slim list row for the same lead is empty where this one has text", | |
| not any(d.get(f) and by_id.get(pick["id"], {}).get(f) for f in SLIM_DEFERRED)) | |
| print("\nBell poll (GET /notifications/unread-count)") | |
| n, n_med = timed(client, "/notifications/unread-count", None, max(args.repeat, 5)) | |
| print(f" {n['status']} first {n['ms']:.0f}ms, median {n_med:.0f}ms -> {n['data']}") | |
| print(" (with only the API key there is no signed-in user, so count is 0; the latency is what matters here)") | |
| check("200", n["status"] == 200, str(n["status"])) | |
| print(f"\n{'FAILED: ' + str(FAILED) if FAILED else 'All checks passed.'}") | |
| return FAILED | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |