Поток uWSGI имеет существующий запущенный цикл событий, который вызывает исключение Django SynchronousOnlyOperation

У меня есть два представления Django,

def view_that_accesses_orm(request): # say end point /useorm
  user = User.objects.first()
  ...

и

def view_that_creates_event_loop(request): # say endpoint /createloop
  client = AsycProvider()
  ... # do stuff with client

и AsyncProvider - это что-то вроде

class AsyncProvider:
  def __init__(self):
    try:
      self.__loop = asyncio.get_event_loop()
    except RuntimeError as e:
      print(e) #no running event loop
      self.__loop = asyncio.new_event_loop()
      asyncio.set_event_loop(self.__loop)
    self.__session = aiohttp.ClientSession(loop=self.__loop)

  ... # other operations with asyncio.run_until_complete, asyncio.gather, and self.__session

Теперь проблема в том, что, скажем, если у меня есть 1 процесс в uWSGI и 2 потока. Тогда они будут обслуживать запросы по кругу.

Итак, сценарий таков:

  1. User hits /createloop (given thread 1)
  2. User hits /useorm (given thread 2)
  3. User again hits /useorm (given thread 1)

Теперь третий сценарий, иногда цикл событий запущен, и поскольку Django 3.x обнаруживает запущенный цикл событий и запрещает нам доступ к ORM, я получаю django.core.exceptions.SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async. исключение.

Я не уверен, как цикл событий не останавливается и сохраняется в потоке.

Пожалуйста, объясните, что именно может быть причиной этого и что следует исправить?

Проблема заключалась в том, что где-то внутри метода AsyncProvider использовался метод asyncio.set_event_loop(self.__loop).

Таким образом, на один и тот же экземпляр цикла ссылались в разных потоках (новый цикл не создавался). Теперь, поскольку в этом цикле иногда выполнялась логика, а также если в потоке (который ссылался на этот цикл) происходило обращение к ORM, Django бросал SynchronousOnlyOperation, поскольку мог обнаружить работающий цикл событий.

Решил ее, удалив asyncio.set_event_loop(self.__loop) в методе.

Вернуться на верх