Запрос Django ORM внутри сельдерея задача: синхроннооперация: Вы не можете вызвать это из асинхронного контекста — используйте поток или sync_to_async

#python #django #celery #django-orm #gevent

Вопрос:

Мы используем задания сельдерея наряду с Django, и в рамках различных задач сельдерея есть несколько случаев, когда задача сельдерея заключается в чтении и записи в базу данных через ORM Django.

Время от времени при использовании ORM внутри задачи сельдерея задачи бросают:

Synchronousonlyоперация: Вы не можете вызвать это из асинхронного контекста — используйте поток или sync_to_async.

Мне кажется странным, что это происходит иногда, а не каждый раз, когда запрос делается через ORM? Во-вторых, при попытке решить эту проблему в соответствии с предложениями в документации Django здесь:

https://docs.djangoproject.com/en/3.2/topics/async/

вот так: Пример использования sync_to_asynch

Я сталкиваюсь с другой проблемой: ошибка типа: объект «сопрограмма» не может быть повторен

Мои вопросы таковы:

  1. Почему эта проблема возникает только время от времени, а не каждый раз, когда я выполняю запрос с помощью ORM внутри задачи сельдерей?
  2. Есть ли способ решить эту проблему?

Окружающая среда

Задачи сельдерея выполняются с помощью gevent следующим образом: сельдерей -Работник задач-P gevent -c 10 -l ИНФОРМАЦИЯ -E

Python 3.8

Джанго 3.1.4

Сельдерей 5.1.0

Комментарии:

1. Убедитесь, что вы не передаете объекты, такие как модели, через аргументы, если вам нужно передать данные, связанные с базой данных, просто передайте pk , а затем в функции выполните запрос, потому что сельдерей не работает нормально с объектами ORM, переданными в качестве параметров.

2. Спасибо! Однако с этим уже разобрались, и в данном случае это не проблема.

3. Пожалуйста, укажите соответствующий код непосредственно в своем вопросе, а также полную обратную связь.

Ответ №1:

sync_to_async() вернет сопрограмму. Как указывает ошибка типа, вы не можете выполнять итерацию сопрограммы напрямую. Вы должны await это сделать, как показано в примере.

Из документов:

 from asgiref.sync import sync_to_async

results = await sync_to_async(Blog.objects.get, thread_sensitive=True)(pk=123)
 

На вашей фотографии вы не await звоните.

Комментарии:

1. Спасибо! Это абсолютно верно. Следующая проблема, с которой я сталкиваюсь, если жду вызова, заключается в том, что для этого требуется, чтобы объявление функции было асинхронным. Проблема, которую я думаю, заключается в том, что Celery gevent порождает зеленые потоки (поскольку я использую gevent), и на самом деле это не асинхронный контекст, который можно объявить с помощью ключевого слова async, поскольку сельдерей заботится об этом асинхронном порождении зеленого потока. Есть ли какой-либо способ обойти это, или мне нужно прибегнуть к использованию prefork, а не gevent в настройках сельдерея?