Toàn bộ event loop bị chặn trong suốt thời gian chờ HTTP, nên mọi request khác của tiến trình đó cũng phải xếp hàng chờ theo.
Lý do: hàm async def chạy trực tiếp trên event loop. Loop chỉ chuyển sang việc khác tại các điểm await. Một lời gọi blocking (requests, time.sleep, driver DB đồng bộ, xử lý dữ liệu nặng) giữ luôn thread đang chạy loop, không nhả quyền điều khiển.
@app.get("/bad")
async def bad():
return requests.get(URL).json() # blocks the loop
@app.get("/good")
async def good():
async with httpx.AsyncClient() as client:
r = await client.get(URL) # yields back to the loop
return r.json()Ba hướng sửa:
- Đổi sang client bất đồng bộ (httpx.AsyncClient, asyncpg, aioredis) — tốt nhất.
- Đổi khai báo thành def (không async): Starlette sẽ tự đẩy hàm sang threadpool.
- Giữ async def nhưng bọc phần đồng bộ: await anyio.to_thread.run_sync(blocking_fn) hoặc await asyncio.to_thread(blocking_fn).
Dấu hiệu nhận biết khi vận hành: p99 latency của mọi endpoint tăng cùng lúc, kể cả /health.