Skip to content

Archive

Asyncio

7 articles
Python 09 Sep 2026 9 min read

Shut Down asyncio Worker Queues Cleanly

Asynchronous worker pools often start with a simple pattern: producers put jobs into an asyncio.Queue, consumers loop over get(), and the application waits for join() before exiting. The awkward part is shutdown. Older designs commonly put one sentinel value into the queue for each worker, cancel consumers after join(), or maintain a separate stop event. Each approach can work, but each adds a second protocol beside the queue itself. Python 3.13 added asyncio.Queue.shutdown() and the asyncio.QueueShutDown exception. They let the queue represent its own lifecycle: open for producers, shutting down while existing work drains, and finally closed to consumers.

Python 09 Sep 2026 12 min read

Debug Running Asyncio Services with pstree and ps in Python 3.14

An asynchronous service can be alive while making no useful progress. The process still responds to signals. CPU usage may be low. The event loop is still running. Yet a request, worker, or shutdown path appears stuck somewhere inside a chain of coroutines. Traditional stack traces are only part of the answer. An asyncio application is organized around tasks and await relationships, so the useful question is often not merely “where is this thread?” but “which task is waiting for which other task?”

Python 09 Sep 2026 10 min read

Control asyncio Task Startup with eager_start

Creating an asyncio task usually feels like a clean scheduling boundary: call asyncio.create_task(), keep the returned task, and let the event loop run the coroutine soon. Python also supports eager task execution, where a coroutine can begin running immediately during task creation. Python 3.14 makes that choice directly available through the eager_start keyword on asyncio.create_task() and through task-group task creation. That can remove scheduling overhead for coroutines that often complete without blocking. It can also change program ordering in ways that matter much more than the performance gain.

Python 08 Sep 2026 6 min read

Keep Request State Local with Python contextvars

Passing a request ID through every function is explicit, but after a few layers it can become noise. Logging is the example I keep running into: the logger needs the request ID, while most business functions do not actually care about it. A global variable looks tempting until two requests run concurrently. threading.local() fixes a different problem, but one event-loop thread can execute many asyncio tasks. Python’s contextvars module is designed for this kind of context-local state.

Python 08 Sep 2026 7 min read

Budget Async Work with asyncio.timeout

Timeouts in asynchronous programs are easy to scatter and surprisingly hard to compose. A service call gets five seconds, a database query gets five more, and a retry gets another five. Each individual limit looks reasonable, yet the whole request can run far beyond the caller’s budget. Python 3.11 added asyncio.timeout(), an asynchronous context manager that makes a different model practical: put a time budget around a block of work, not just around one awaitable.

Python 02 Sep 2026 6 min read

Structured Concurrency in Python with asyncio.TaskGroup

Concurrent code becomes difficult to reason about when tasks can outlive the operation that created them. A request handler may return while background tasks are still running, or one task may fail while its siblings continue doing work that is no longer useful. Python’s asyncio.TaskGroup, available since Python 3.11, provides structured concurrency for related asynchronous tasks. Tasks created inside the group belong to a clear lifetime: leaving the async with block waits for them, and failures are handled as a group rather than as detached background events.

Python 02 Sep 2026 4 min read

Request-Scoped State in Python with contextvars

Applications often need small pieces of context to follow a request through several layers: a request ID, tenant identifier, locale, or tracing field. Passing every value through every function is explicit, but can become noisy when the value is cross-cutting rather than part of the function’s business input. Python’s contextvars module provides context-local state designed to work with asynchronous code. Why a normal global is unsafe A module-level variable is shared by all concurrent requests: