use-async-io-without-blocking-the-event-loop
Use when an async service shows latency equal to a dependency's response time — find the blocking call on the event loop and move it to a thread or process pool.
- License 许可
- MIT
- 最近更新
- 2026-11-14
- Stars
- ★ 81
- Forks
- 17
01 前言 Front Matter
- name
- use-async-io-without-blocking-the-event-loop
- description
- Use when an async service shows latency equal to a dependency's response time — find the blocking call on the event loop and move it to a thread or process pool.
02 资源文件 3
- 许可证LICENSE1.0 KB
- 参考文档SOURCE.md370 B
- 参考文档TRUST.auto.yaml2.1 KB
03 SKILL.md 正文
Use async I/O without blocking the event loop
In async runtimes (Node, Python asyncio), one synchronous call blocks every other request on that loop. The signature is stark: p99 latency tracks an unrelated operation's duration, and throughput does not improve when that dependency gets faster.
Procedure
- Detect the block. Python: set
PYTHONASYNCIODEBUG=1andloop.slow_callback_duration = 0.05, or watch an event-loop-lag metric. Node: a latency histogram aroundsetImmediate, plus--trace-warnings. - Find synchronous calls on async paths:
requests,urllib,time.sleep, direct file reads,subprocess.run, CPU-heavy loops, and high-costbcryptorhashlibon large inputs. - Move blocking I/O to a thread pool, wrapping the call:
await loop.run_in_executor(None, blocking_fn, arg)
await asyncio.to_thread(requests.get, url)
Or switch to a native async client: httpx.AsyncClient, aiofiles, asyncpg.
4. Move CPU-bound work to a process pool, not a thread pool — the GIL serializes Python bytecode across threads:
loop.run_in_executor(ProcessPoolExecutor(), crunch, data)
- For Node, drop to a worker thread or child process for CPU work;
fs.readFileSyncandcrypto.pbkdf2Syncblock the whole server. - Treat event-loop lag as a first-class metric (
event_loop_lag_seconds) and alert when it exceeds a few tens of milliseconds; lag is the direct measure of blocking. - Profile with a live sampler (
py-spy,--prof) to name the blocking function rather than guessing.
Pitfalls
- Calling
asyncio.runinside a running loop, which raises "loop already running" — a symptom of a sync library in disguise. - Using a thread pool for CPU work and expecting speedup; the GIL serializes it.
- Covering the symptom with a bigger thread pool instead of removing the blocking call.
- A synchronous ORM or driver in an async framework; a thread pool cannot fully hide it.
- Missing
awaiton a coroutine, so it never runs and returns a coroutine object. - Blocking in a startup hook or signal handler, delaying the whole process before it serves anything.
Verification
curl -s localhost:9090/metrics | grep event_loop_lag_seconds
python -X dev -c "import asyncio; asyncio.get_event_loop().slow_callback_duration = 0.05"
# pass: lag p99 < 50ms under load; throughput scales when the dependency speeds up
Report the blocking call found, where it was moved (thread vs process pool), the loop-lag metric before and after, and the throughput change.
04 同仓库更多 Skills
recipe-performance
Diagnose a specific MetaMask user flow's real-app performance and Sentry critical-user-flow telemetry with deterministic mm-harness recipes. Use for CUF boundaries, cold/warm/background-resume behavior, latency regressions, lifecycle tags, duplicate or blocking network requests, long tasks, layout/paint cost, screen redraws, React rerenders, or browser trace analysis where tests alone cannot prove the real app.
mexc-spot-websocket
MEXC Spot WebSocket workflows for public streams, protobuf handling, depth maintenance, listen keys, and private streams without exposing API keys.
mexc-spot-rest
MEXC Spot REST workflows for public data, signed account requests, orders, wallet/admin actions, and secure signing without exposing API keys.
mexc-futures-websocket
MEXC Futures WebSocket workflows for public streams, private account/order/position streams, ping/pong, subscriptions, and depth maintenance.
0xklen-version-and-refresh-sanctions-lists
暂无简介
verify-tests-cover-changed-lines
Use when a PR adds code that the existing suite may not exercise. Measures patch coverage — the share of newly changed lines hit by tests — before approving.