Django post_save信号和芹菜任务之间可能存在竞争条件



在django 2.0应用程序中,我有一个名为Document的模型,它可以将图像上传并保存到文件系统。这部分有效。我正在使用对图像进行面部识别https://github.com/ageitgey/face_recognition在芹菜(v4.2.1(任务中。我将图像的document_id传递给芹菜任务,这样人脸识别任务就可以找到要处理的图像。如果我在图像保存后通过DocumentAdmin操作手动调用face_recognition任务,这一切都会很好。

我试着从models.py中的(models.signals.post_save, sender=Document)方法调用face_recognition任务,在face_recoinction的芹菜任务中,我从这一行得到了一个错误:

document = Document.objects.get(document_id=document_id)

错误为:

[2018-11-26 16:54:28,594: ERROR/ForkPoolWorker-1] Task biometric_identification.tasks.find_faces_task[428ca39b-aefb-4174-9906-ff2146fd6f14] raised unexpected: DoesNotExist('Document matching query does not exist.',)
Traceback (most recent call last):
File "/home/mark/.virtualenvs/memorabilia-JSON/lib/python3.6/site-packages/celery/app/trace.py", line 382, in trace_task
R = retval = fun(*args, **kwargs)
File "/home/mark/.virtualenvs/memorabilia-JSON/lib/python3.6/site-packages/celery/app/trace.py", line 641, in __protected_call__
return self.run(*args, **kwargs)
File "/home/mark/python-projects/memorabilia-JSON/biometric_identification/tasks.py", line 42, in find_faces_task
document = Document.objects.get(document_id=document_id)
File "/home/mark/.virtualenvs/memorabilia-JSON/lib/python3.6/site-packages/django/db/models/manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/mark/.virtualenvs/memorabilia-JSON/lib/python3.6/site-packages/django/db/models/query.py", line 403, in get
self.model._meta.object_name
memorabilia.models.DoesNotExist: Document matching query does not exist.

此外,这种错误并非总是发生,只是偶尔发生。在剩下的时间里,这个过程是有效的;即保存图像并识别人脸。

我覆盖DocumentAdmin类中的save_model,但这只是为另一个模型中的图像保存了一些元数据。最后一行是对super().save_model(request, obj, form, change)的调用,所以我假设在那之后调用postrongave信号。

在我看来,在将模型保存到数据库和芹菜任务在数据库中查询新创建的document_id之间存在竞争条件。我以为保存后的信号直到模型保存后才被激活?

为了解决这种可能的比赛条件,我是否必须在芹菜任务face_recognition中添加一些人为的延迟,或者我错过了其他东西?

谢谢!

标记

检查保存Document模型的函数。它被封装在某个原子块中,或者您将atomic_REQUESTS设置为True。因此,当调用post_save时,事务尚未提交。因此,您的模型在那个时刻并没有真正保存到数据库中。

似乎有时信号会超过您的数据库写入速度!作为一种有害的变通方法,你可以晚一点运行芹菜任务,只需几秒钟。

以下是操作方法:

your_task.apply_async(
[document_id],
countdown=5 # this is the delay in seconds - you can adapt it accordingly
)

如果这对你的案子有效,请告诉我!

正如@UnholyRaven的回答中所提到的,问题与执行任务时未提交的事务有关。

为了解决这个问题,我们可以使用Django的transaction.on_commit 在事务提交上调度任务

@receiver(post_save, sender=Document):
find_faces(sender, instance, created, **kwargs):
transaction.on_commit(lambda: find_faces_task.apply_async([instance.id]))

相关内容

最新更新