Django post_save作为芹菜任务的奇怪行为



我有以下代码:

@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,它将毫不延迟地生成一个异常,允许您处理这种情况。

相关内容

  • 没有找到相关文章

最新更新