Python contextvars: ID запроса в asyncio-задачах
ContextVar хранит значение для текущего асинхронного контекста, а не в одной общей глобальной переменной. asyncio копирует контекст при создании задачи, поэтому параллельные запросы могут безопасно использовать разные request ID. Устанавливайте значение на границе обработки и сбрасывайте его в finally, чтобы оно не просочилось в последующую работу.
Передать request ID дочерним задачам
import asyncio
from contextvars import ContextVar
request_id = ContextVar("request_id", default="-")
async def log_step(step: str) -> None:
await asyncio.sleep(0)
print(f"{request_id.get()} {step}")
async def handle_request(value: str) -> None:
token = request_id.set(value)
try:
await asyncio.gather(
log_step("load"),
log_step("save"),
)
finally:
request_id.reset(token)
async def main() -> None:
await asyncio.gather(
handle_request("req-a"),
handle_request("req-b"),
)
asyncio.run(main())В выводе шаги одного запроса сохранят общий ID, даже если задачи выполняются вперемешку. Context variables поддерживаются asyncio с Python 3.7. В веб-приложении ставьте значение в middleware и сбрасывайте в том же контексте; для фоновой задачи передавайте ID явно или создавайте её внутри нужного контекста.
Документация: contextvars, asyncio tasks.