Async io patterns
Skill Amey-Thakur/AI-SKILLS/skills/performance/async-io-patterns
Plug-and-play skills and prompts for every AI coding agent
npx -y skills add Amey-Thakur/AI-SKILLS --skill async-io-patternsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 21 days oldThe repository was created 21 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Structure async code so the event loop stays free, slow producers apply backpressure, and blocking calls never stall unrelated work. Use when writing or debugging async IO and throughput is low, latency is spiky, or the loop appears to hang.
SKILL.md
2.8 KB, 622 tokens by cl100k_base, as published. Nobody here has run it
Async IO patterns
Async concurrency runs thousands of IO operations on one thread by never waiting idly: while one request is in flight, the event loop advances another. That model breaks the instant one task blocks the thread, or one producer outruns its consumer and floods memory. The failures are quiet until the loop freezes under load.
Method
- Never block the event loop. A synchronous call inside a coroutine (a
requests.get, atime.sleep, a CPU-heavy loop, a blocking DB driver) stops every other task on that loop. Push it to a thread pool withasyncio.to_threadorloop.run_in_executor, or swap in an async-native client (aiohttp,asyncpg). - Await concurrently, not in sequence. Ten awaits in a
forloop run one at a time. Gather them:await asyncio.gather(*tasks)for a fixed set, or feed them through a bounded worker pattern so all ten IO waits overlap. - Bound in-flight work with a semaphore. Firing 10,000 requests at once
exhausts sockets and gets you rate-limited. Wrap each task in
async with semaphore:sized to the downstream limit (say 50) so only that many are ever active. - Apply backpressure at the queue. When a producer is faster than its
consumer, an unbounded
asyncio.Queuegrows without limit. Give it amaxsizesoputsuspends the producer until the consumer catches up, and memory stays flat. - Always time out external calls. A hung socket with no timeout ties up a
task forever. Wrap network awaits in
asyncio.wait_for(coro, timeout=...)or the client's own timeout, so a dead peer fails fast instead of leaking a task. - Handle cancellation and gather errors. One failing task in
gathercancels its siblings unless you passreturn_exceptions=True. Decide the policy, and letCancelledErrorpropagate rather than swallowing it, or you leak tasks on shutdown.
Litmus tests
- Under load, does the loop keep serving other requests, or does one slow call freeze all of them?
- Is every batch of independent awaits gathered rather than run in a loop?
- Is the number of simultaneously in-flight operations capped by a semaphore or bounded queue?
- Does every external await carry a timeout, so a dead peer cannot pin a task?
Boundaries
This skill covers correct use of a single-threaded event loop and its backpressure. Choosing how many OS threads or processes to run alongside it is concurrency-tuning's job. Framework-specific lifecycle rules (ASGI startup, task groups, structured-concurrency libraries) defer to that framework's documented contract.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.