"""四视图 + 看板的首屏耗时实测(1000 条任务)。 不是压测工具,只回答一个问题:**1000 条之后,点开还会不会卡**。 跑之前先起后端:`uvicorn app.main:app --port 8100`(连开发库)。 python scripts/bench_views.py """ from __future__ import annotations import asyncio import statistics import time import uuid from datetime import datetime, timedelta, timezone import httpx BASE = "http://127.0.0.1:8100/api/v1" TOTAL = 1000 REPEAT = 15 async def login(client: httpx.AsyncClient) -> tuple[str, dict[str, str]]: phone = f"137{uuid.uuid4().int % 100_000_000:08d}" sent = await client.post(f"{BASE}/auth/sms/send", json={"phone": phone}) code = sent.json()["data"]["dev_code"] out = await client.post(f"{BASE}/auth/login", json={"phone": phone, "code": code}) token = out.json()["data"]["tokens"]["access_token"] return token, {"Authorization": f"Bearer {token}"} async def main() -> None: async with httpx.AsyncClient(timeout=30.0) as client: _, headers = await login(client) ws = await client.post(f"{BASE}/workspaces", json={"name": "性能实测"}, headers=headers) ws_id = ws.json()["data"]["id"] now = datetime.now(timezone.utc) statuses = ["todo", "doing", "blocked", "done", "inbox"] print(f"造 {TOTAL} 条任务……") started = time.perf_counter() for index in range(TOTAL): payload = { "title": f"测试任务 {index:04d} " + ("周报" if index % 7 == 0 else "跟进"), "status": statuses[index % len(statuses)], "priority": index % 4, "due_at": (now + timedelta(days=(index % 10) - 5)).isoformat(), "tag_names": ["批量", f"组{index % 5}"], } await client.post(f"{BASE}/workspaces/{ws_id}/items", json=payload, headers=headers) print(f"写入耗时 {time.perf_counter() - started:.1f}s") targets = { "今天": f"/workspaces/{ws_id}/items?view=today&limit=30", "进行中": f"/workspaces/{ws_id}/items?view=active&limit=30", "收件箱": f"/workspaces/{ws_id}/items?view=inbox&limit=30", "全部": f"/workspaces/{ws_id}/items?view=all&limit=30", "搜索(周报)": f"/workspaces/{ws_id}/items?view=all&q=%E5%91%A8%E6%8A%A5&limit=30", "看板": f"/workspaces/{ws_id}/items/board", } print("\n| 视图 | 首次 | P50 | P95 | 返回条数 |") print("|---|---|---|---|---|") for label, path in targets.items(): samples: list[float] = [] count = 0 for attempt in range(REPEAT): t0 = time.perf_counter() resp = await client.get(f"{BASE}{path}", headers=headers) samples.append((time.perf_counter() - t0) * 1000) if attempt == 0: body = resp.json()["data"] if "columns" in body: count = sum(len(v) for v in body["columns"].values()) else: count = len(body["items"]) samples.sort() p50 = statistics.median(samples) p95 = samples[min(len(samples) - 1, int(len(samples) * 0.95))] print(f"| {label} | {samples[0]:.1f} ms | {p50:.1f} ms | {p95:.1f} ms | {count} |") print( "\n注:含网络往返与鉴权中间件;本机 127.0.0.1,所以这是**下限**," "真实用户还要加速度。" ) if __name__ == "__main__": asyncio.run(main())