我在从另一个芹菜任务执行芹菜任务时遇到问题。
这是有问题的片段(数据对象已经存在于数据库中,其属性只是在finalize_data函数中更新):
def finalize_data(data):
data = update_statistics(data)
data.save()
from apps.datas.tasks import optimize_data
optimize_data.delay(data.pk)
@shared_task
def optimize_data(data_pk):
data = Data.objects.get(pk=data_pk)
#Do something with data
在optimize_data函数中调用失败,并且“数据匹配查询不存在。”
如果我在finalize_data函数中调用pk函数检索它可以正常工作。如果我将celery任务调用延迟一段时间,它也可以正常工作。
这一行:
optimize_data.apply_async((data.pk,), countdown=10)
而不是
optimize_data.delay(data.pk)
工作正常。但我不想在我的代码中使用hacks。是否有可能.save()调用异步阻止对该行/对象的访问?
答案 0 :(得分:4)
我猜测你的调用者是在芹菜开始处理任务之前没有提交的事务中。因此,芹菜无法找到记录。这就是为什么添加倒计时使它工作。
1秒倒计时可能与您示例中的10秒倒计时一样有效。我在整个代码中使用了1秒倒计时来处理这个问题。
另一种解决方案是停止使用交易。
答案 1 :(得分:3)
我知道这是一个很老的帖子,但我今天偶然发现了这个问题。李的回答向我指出了正确的方向,但我认为今天存在更好的解决方案。
使用Django提供的on_commit
处理程序可以解决这个问题,而不会在代码中使用一种可能不太直观的错误的计数倒计时方法。
我不确定问题发布时是否存在,但我只是发布答案,以便将来来这里的人知道替代方案。
答案 2 :(得分:1)
您可以使用on_commit
挂钩来确保直到事务提交后才触发celery任务?
DjangoDocs#performing-actions-after-commit
这是Django 1.9中添加的功能。
from django.db import transaction
def do_something():
pass # send a mail, invalidate a cache, fire off a Celery task, etc.
transaction.on_commit(do_something)
您还可以将函数包装在lambda中:
transaction.on_commit(lambda: some_celery_task.delay('arg1'))
您传递的函数将在成功调用on_commit()的假设数据库写入之后立即被调用。
如果在没有活动交易的情况下调用on_commit(),则回调将立即执行。
如果假设的数据库写操作被回滚(通常是在atomic()块中引发未处理的异常时),则函数将被丢弃并且从不调用。