我有以下代码:
@receiver(post_save, sender=SomeModel, dispatch_uid="build")
def handle_creation(sender, instance, created, **kwargs):
if created == True:
build.delay(instance)
@task()
def build(instance):
instance.status = 'Processing'
instance.save()
#some heavy instructions here
#. . . .
#. . . .
instance.status = 'Finished'
instance.save()
我产生以下错误:
IntegrityError: duplicate key value violates unique constraint DETAIL: Key (id)=(13) already exists.
但如果我去掉第一个instance.save()
,一切都很好。芹菜处理任务时,sql指令似乎不完整。如何修复?
谢谢。
您的代码有两个问题。
第一个是最大的,您正在将对象实例传递给任务——这在芹菜文档中被明确标记为错误的方法,基本上您所做的是在某种状态下序列化对象,并将其传递给芹菜进行处理;但与此同时,这个目标可能会改变;作为一个解决方案,您应该将对象id作为参数传递,这样芹菜任务就可以重新获取它:
build.delay(instance.pk)
...
@task
def build(my_key):
instance = SomeModel.objects.get(pk=my_key)
instance.status = 'Processing'
instance.save()
第二个问题本质上是微妙的,很少出现在雷达上。您的代码的第一部分可以在事务中调用,这意味着可能会出现这样的情况,即您的任务(在芹菜中)将比事务提交更快,然后您的模型将首先保存在芹菜任务中,然后按事务保存-这是一个问题。
如果您按照上面的建议更改代码,第二个问题所描述的情况可能不会发生,或者它将显示不同的错误。
为了避免这样的问题,最好从transaction.oncommit
处理程序(在1.9版本中引入Django)调用芹菜任务
还有一条评论,我可以看到你正在改变对象的状态:
instance.status = 'Processing'
很可能是信息性的,但也可能被用作锁定机制。。。QuerySet
中有一个非常好的选项select_for_update
方法,它将允许您在事务期间锁定对象。这对于芹菜任务来说尤其好,当你这样做的时候:
instance = SomeModel.objects.select_for_update().get(pk=my_key)
它将停止您的任务等待其他任务完成(不要忘记将@transaction.atomic
置于此任务之上)
若您将nowait=True
传递给select_for_update
,它将毫不延迟地生成一个异常,允许您处理这种情况。