Member-only story
12 asyncio Mistakes That Make a Python Service Slower, Measured
One time.sleep in an async handler froze every other request for 1.2 seconds. Twelve asyncio mistakes like it, from unbounded gather to a client that took 559 ms to construct, each run on Python 3.13.
Ten coroutines, each pretending to make a 100 ms call, started together with asyncio.gather:
await asyncio.sleep(0.1) total 0.11 s worst loop stall 6 ms
time.sleep(0.1) total 1.21 s worst loop stall 1196 msThe first line is what async is for: ten waits overlap, and the whole batch takes about as long as one. The second line is the same code with time.sleep instead of await asyncio.sleep. It ran the ten calls one after another and blocked the event loop for 1.2 seconds. During that time, every other request in the process waited too, including health checks.
The belief behind most asyncio bugs is that async def makes code concurrent. It doesn't. An event loop is one thread taking turns, and it only switches at an await on something that actually waits without blocking. Anything that holds the thread without awaiting, a blocking library call, a CPU-heavy loop, a slow constructor, holds up everybody. And anything you start without limits or without keeping a handle on it can fail in ways that never reach your logs.










