【问题标题】:Celery and transaction.atomicCelery 和 transaction.atomic
【发布时间】:2015-01-12 14:08:08
【问题描述】:

在一些 Django 视图中,我使用这样的模式来保存对模型的更改,然后根据新的模型数据进行一些异步更新(例如生成图像,进一步更改模型)。 mytask 是一个芹菜任务:

with transaction.atomic():
    mymodel.save()
    mytask.delay(mymodel.id).get()

问题是任务永远不会返回。查看 celery 的日志,任务排队(我在日志中看到“已接收任务”),但它从未完成。如果我将mytask.delay...get 调用移出事务,它会成功完成。

transaction.atomic 和 celery 之间是否存在一些不兼容?在 Django 1.6 或 1.7 中,我是否可以在一个事务下同时进行常规模型更新和来自单独任务流程的更新?

我的数据库是 postgresql 9.1。我正在使用 celery==3.1.16 / django-celery 3.1.16,amqp==1.4.6,Django==1.6.7,kombu==3.0.23。 broker后端是amqp,队列是rabitmq。

【问题讨论】:

    标签: django postgresql transactions celery


    【解决方案1】:

    作为@dotz mentioned,生成异步任务并立即阻塞并继续等待直到完成几乎没有用处。

    此外,如果您以这种方式附加到它(最后的.get()),您可以确定您的工作人员不会看到刚刚进行的mymodel 实例更改,因为它们不会被提交然而 - 记住你还在 atomic 块内。

    您可以做的(从 Django 1.9 开始)是使用 django.db.transaction.on_commit 钩子将任务延迟到提交当前活动事务之后:

    from django.db import transaction
    
    with transaction.atomic():
        mymodel.save()
        transaction.on_commit(lambda:
            mytask.delay(mymodel.id))
    

    我经常在我的post_save 信号处理程序中使用这种模式,这些处理程序会触发对新模型实例的一些处理。例如:

    from django.db import transaction
    from django.db.models.signals import post_save
    from django.dispatch import receiver
    from . import models   # Your models defining some Order model
    from . import tasks   # Your tasks defining a routine to process new instances
    
    @receiver(post_save, sender=models.Order)
    def new_order_callback(sender, instance, created, **kwargs):
        """ Automatically triggers processing of a new Order. """
        if created:
            transaction.on_commit(lambda:
                tasks.process_new_order.delay(instance.pk))
    

    但是,如果数据库事务失败,您的任务将不会被执行。这通常是期望的行为,但请记住这一点。

    编辑:以这种方式注册 on_commit celery 任务实际上更好(没有 lambda):

    transaction.on_commit(tasks.process_new_order.s(instance.pk).delay)
    

    【讨论】:

    • 非常感谢 Celery 的签名。出于某种原因,使用 lambda 的语法会产生不正确的行为 - Celery 使用错误的参数运行任务(双打相同的参数)。
    • Lambda 对我来说工作得很好,但是这个.s 模式更干净!谢谢!
    【解决方案2】:

    “单独的任务”= 由工人运行的东西。

    "Celery worker" = 另一个进程。

    我不知道有任何方法可以让您在 2 个或多个进程之间共享单个数据库事务。您想要的是在该事务中以同步方式运行任务,然后等待结果......但是,如果这是您想要的,为什么还需要任务队列?

    【讨论】:

    • 好吧,在 Windows 上,DTC 支持它。
    • 为什么我需要任务队列:stackoverflow.com/a/7543682/85461。由于来自与 WSGI 交互的 java 子进程的 OS 信号存在一个深奥的问题,即使您需要同步结果,有时也可能需要使用单独的进程。
    【解决方案3】:

    Celery 任务中有一些竞争条件。 我认为here 是对您问题的解释。

    查看这些文档。还有一些像 django-celery-transactions 这样的包可以帮助你解决你的问题。

    【讨论】:

    • 谢谢——那些文档确实指出了问题的潜在根源。 django-celery-transactions 看起来不错,如果我想在常规模型更新引发异常时跳过异步任务——但我正在寻找一种在异步任务失败时回滚整个事务的方法。据我所知,django-celery-transactions 将任务中的工作留给单独的事务处理。
    猜你喜欢
    • 2014-03-18
    • 1970-01-01
    • 2021-12-19
    • 2014-03-16
    • 2017-04-07
    • 1970-01-01
    • 1970-01-01
    • 2014-03-27
    • 2017-08-25
    相关资源
    最近更新 更多