在 celery 任务中查询 Django ORM:SynchronousOnlyOperation:您不能从异步上下文中调用它 - 使用线程或 sync_to_async

Posted

技术标签:

【中文标题】在 celery 任务中查询 Django ORM:SynchronousOnlyOperation:您不能从异步上下文中调用它 - 使用线程或 sync_to_async【英文标题】:Querying Django ORM inside celery task: SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async 【发布时间】:2021-12-23 03:55:19 【问题描述】:

我们在 Django 旁边使用 celery 作业,在不同的 celery 任务中,有好几次 celery 任务通过 Django 的 ORM 读取和写入数据库。

每隔一段时间在 celery 任务中使用 ORM 时,任务会抛出:

SynchronousOnlyOperation:您不能从异步上下文中调用它 - 使用线程或 sync_to_async。

我觉得奇怪的是它有时会发生,而不是每次通过 ORM 进行查询时?其次,当尝试按照此处 Django 文档中的建议解决它时:

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

像这样:Example of sync_to_asynch ussage

我遇到了另一个问题: TypeError: 'coroutine' object is not iterable

我的问题是:

    为什么这个问题只是偶尔出现,而不是每次我在 celery 任务中使用 ORM 查询时都会出现?

    有办法解决吗?

环境

celery 任务使用 gevent 运行,如下所示: celery -A 任务工作者 -P gevent -c 10 -l INFO -E

Python 3.8

Django 3.1.4

芹菜 5.1.0

【问题讨论】:

如果您需要传递与数据库相关的数据,请确保不要通过 args 传递模型等对象,只需传递 pk 然后在函数中进行查询,因为 celery 没有使用作为参数传递的 ORM 对象可以正常工作。 谢谢!不过,这已经处理过了,在这种情况下不是问题。 请在您的问题中直接包含相关代码以及完整的回溯。 【参考方案1】:

sync_to_async() 将返回一个协程。正如 TypeError 所暗示的,您不能直接迭代协程。你必须await它,如示例所示。

来自文档:

from asgiref.sync import sync_to_async

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

在你的图片中,你没有await这个电话。

【讨论】:

谢谢!这是完全正确的。如果我等待调用,我遇到的下一个问题是它要求函数声明是异步的。我认为当时的问题是 Celery gevent 正在产生绿色线程(因为我使用 gevent),它并不是一个可以用 async 关键字声明的异步上下文,因为 celery 正在处理这个线程异步绿色线程的产生。有什么办法可以解决这个问题,还是我必须在我的 celery 设置中使用 prefork 而不是 gevent?

以上是关于在 celery 任务中查询 Django ORM:SynchronousOnlyOperation:您不能从异步上下文中调用它 - 使用线程或 sync_to_async的主要内容,如果未能解决你的问题,请参考以下文章

如何在 celery 任务中强制 django-orm 中的单个保存的 db 提交

使用 celery 为 Django 应用程序异步执行任务

在 Django Admin 中保存新对象并发送到 Celery 任务后,匹配查询不存在

Django celery 任务:新创建的模型 DoesNotExist

Celery学习--- Celery 最佳实践之与django结合实现异步任务

在 Django 1.11 中将 QuerySet 传递给 Celery 任务