Короткий ответ
Главный риск — скрытые блокирующие вызовы (синхронный ORM, сторонние SDK без async-версии), которые при наивном переносе блокируют весь event loop и снижают, а не повышают производительность; реальный выигрыш появляется только если весь путь запроса действительно асинхронный, а нагрузка преимущественно I/O-bound.
Как это работает подробнее
Django с 3.1+ поддерживает async view и ASGI, но исторический ORM синхронный — вызов внутри async def view без sync_to_async выбросит ошибку либо, при обёртке, выполнится в отдельном потоке, что добавляет overhead переключения, а не даёт истинного ускорения.
Пример кода
example.py
# Намеренно показан антипаттерн при наивном переносе:
async def get_order(request, order_id):
order = Order.objects.get(id=order_id) # ОШИБКА: блокирующий вызов в event loop
return JsonResponse({"id": order.id})
# Корректный промежуточный вариант:
from asgiref.sync import sync_to_async
async def get_order(request, order_id):
order = await sync_to_async(Order.objects.get)(id=order_id)
return JsonResponse({"id": order.id})Что использовать на практике
- Рекомендация. нужен аудит всех внешних вызовов на пути запроса — для каждого найти async-альтернативу или вынести в
sync_to_async; миграцию делают постепенно, view за view, с нагрузочным тестированием каждого изменённого участка. - Компромисс. полный переход с переписыванием критического пути даёт потенциальный выигрыш при высокой I/O-bound конкурентности, но требует значительных трудозатрат и риска регрессий; выборочный переход самых нагруженных I/O-bound эндпоинтов снижает риск, но не даёт системного выигрыша. Критерий выбора — доля I/O-bound нагрузки и ресурсы на полноценное async-покрытие пути запроса.
Подводные камни
- Риск. переводить view в
async def, оставляя внутри синхронные блокирующие вызовы без оборачивания — замедляет обработку для всех одновременных запросов; ожидать кратного роста для CPU-bound логики, где async не даёт параллелизма вычислений.