Какие причины могут вызывать блокировку event loop в асинхронном сервисе
Что проверяют
Понимаете ли вы, что event loop в asyncio однопоточный и кооперативный: пока корутина не дошла до await, остальные задачи стоят. Следом обычно спрашивают, как найти блокирующий вызов в работающем сервисе и куда вынести тяжёлую работу.
Как отвечать
Причины блокировки:
- синхронный ввод-вывод внутри
async def:requests,time.sleep, синхронные драйверы вродеpsycopg2илиpymysql, чтение больших файлов черезopen. Цикл ждёт, пока вызов вернётся; - CPU-bound работа: разбор большого JSON, хэширование паролей через
bcrypt, pandas, тяжёлые регулярки. Точек переключения здесь нет вообще; - длинные циклы без
await: обработка десятков тысяч элементов подряд. Помогает периодическийawait asyncio.sleep(0), чтобы отдать управление; - синхронные библиотеки в неожиданных местах: SDK, клиент кэша или логгер с медленным обработчиком, который пишет в сеть.
Для I/O берите асинхронные аналоги: httpx или aiohttp, asyncpg, асинхронный режим SQLAlchemy. Неизбежный блокирующий вызов уносите в поток через asyncio.to_thread (с Python 3.9) или loop.run_in_executor. Для CPU-задач нужен ProcessPoolExecutor или отдельный воркер через очередь: в обычной сборке CPython из-за GIL поток не разгрузит процессор.
Найти виновника помогает debug-режим: asyncio.run(main(), debug=True) или PYTHONASYNCIODEBUG=1 пишет в лог колбэки дольше loop.slow_callback_duration, по умолчанию 100 мс. В продакшене снимок стеков даёт py-spy dump.
Если у вас FastAPI, упомяните деталь: обычный def-обработчик выполняется в пуле потоков, а async def прямо в цикле, поэтому блокирующий вызов в async def вредит сильнее.
Где спотыкаются
Думают, что забытый await блокирует цикл. На самом деле корутина просто не выполнится, а Python выдаст RuntimeWarning: coroutine ... was never awaited.
Выносят CPU-задачу в to_thread и ждут ускорения. Цикл освобождается, но вычисления из-за GIL всё так же делят одно ядро.
Путают блокировку цикла с медленным ответом соседа. Долгий await сетевого запроса держит только свою задачу, остальные работают. Если тормозит всё сразу, ищите синхронный вызов.