Tech News
← Back to articles

What Python’s asyncio primitives get wrong about shared state

read original related products more articles

We tried Event, Condition, and Queue. Each one gets closer but still breaks under real concurrency. Here's the observable pattern that finally works.

Coordinating concurrent tasks around shared state is one of the most common problems in Python's asyncio . The standard library gives you asyncio.Event and asyncio.Condition , but each has a gap that only shows up under real concurrency pressure. We hit this while building Inngest's Python SDK, where multiple async handlers coordinate around WebSocket connection state.

This post works through each primitive, shows exactly where it breaks, and iterates toward a solution that handles every case we threw at it.

The scenario

Imagine an async Python app managing a connection that moves through states:

txt disconnected → connecting → connected → closing → closed

One of your concurrent handlers needs to drain pending requests when the connection starts shutting down. It has to wait for the closing state:

state = "disconnected" async def drain_requests ( ) : . . . print ( "draining pending requests" )

Simple enough. Let's see how each stdlib tool handles it.

Attempt 1: Polling

... continue reading