Event loop chạy trên một thread duy nhất. Một lời gọi đồng bộ không nhả quyền điều khiển sẽ chặn toàn bộ loop: mọi request khác — kể cả healthcheck — đứng chờ cho tới khi nó xong. Triệu chứng đặc trưng là p99 tăng đồng loạt ở mọi route chứ không riêng route chậm, trong khi CPU không cao.
Phát hiện:
- Bật loop.set_debug(True) hoặc PYTHONASYNCIODEBUG=1 → asyncio log cảnh báo Executing <Task ...> took N seconds.
- Đo thời gian giữa hai lần loop quay vòng (event loop lag) và đẩy làm metric.
Xử lý theo loại công việc:
import asyncio
import httpx
# 1. I/O: use an async client instead of a blocking one
async def fetch(url):
async with httpx.AsyncClient() as client:
return await client.get(url)
# 2. Blocking sync lib you cannot replace: move it to a thread
result = await asyncio.to_thread(legacy_sdk_call, payload)
# 3. CPU-bound: move it to a process pool, not a thread
loop = asyncio.get_running_loop()
result = await loop.run_in_executor(process_pool, resize_image, blob)Riêng FastAPI/Starlette: khai báo endpoint bằng def thường thì framework tự chạy nó trong threadpool, an toàn cho code đồng bộ; khai báo async def là bạn cam kết mọi thứ bên trong đều non-blocking.
Sai lầm hay gặp nhất là đổi def thành async def "cho hiện đại" nhưng bên trong vẫn gọi driver DB đồng bộ.