芹菜任务中缺少Django相关对象(竞争条件?)

时间:2014-11-11 10:41:11

标签: python django celery race-condition

奇怪的行为,我不知道如何解释。我有一个模型Track,其中包含一些相关的points。我称芹菜任务用点进行一些计算,它们似乎在方法本身完全可达,但在芹菜任务中不可用。

@shared_task
def my_task(track):
    print 'in the task', track.id, track.points.all().count()

def some_method():
    t = Track()
    t.save()
    t = fill_with_points(t)  # creating points, attaching them to a Track
    t.save()
    print 'before the task', track.id, track.points.all().count()
    my_task.delay(t)

打印以下内容:

before the task, 21346, 2971
in the task, 21346, 0

奇怪的是,当我在my_task的第一行或者在调用my_task之前放置time.sleep(10)时,它运作良好,就像有一些竞争条件。但是第一个打印的行明确表示,当points进行选择查询(track.points.all().count())时,{{1}}在数据库中可用。

3 个答案:

答案 0 :(得分:3)

我将假设这是由于事务隔离造成的。

默认情况下,Django事务与请求相关联;当事务处于活动状态时,在提交事务之前,其他任何进程都不会看到更改。如果您正处于save方法的中间,并且在请求完成之前发生了很多其他操作,那么Celery可能会在提交事务之前开始处理任务。您可以通过手动提交或延迟任务来解决此问题。

答案 1 :(得分:0)

你永远不应该将模型对象传递给芹菜任务。这是因为与Django应用程序相比,celery任务中的会话可能会过期(或不同),并且此对象将不会链接到会话,因此可能无法使用/ beheave错误。你应该做的是发送id。所以类似于track_id,然后通过发出查询从数据库中获取对象。这应该最有可能解决你的问题。

@shared_task
def my_task(track_id):
    track = Track.query.get(track_id)  # Or how ever the query should be
    print 'in the task', track.id, track.points.all().count()

def some_method():
    t = Track()
    t.save()
    t = fill_with_points(t)  # creating points, attaching them to a Track
    t.save()
    print 'before the task', track.id, track.points.all().count()
    my_task.delay(t.id)  # Pass the id here, not the object

答案 2 :(得分:0)

所以,我已经使用django-transaction-hooks解决了这个问题。替换我的数据库后端看起来仍然有点可怕,但是django-celery-transactions似乎在Django 1.6中被打破了。现在我的设置如下:

settings.py:

DATABASES = {
    'default': {
        'ENGINE': 'transaction_hooks.backends.postgresql_psycopg2',
        'NAME': 'foo',
        },
    }
SOUTH_DATABASE_ADAPTERS = {'default':'south.db.postgresql_psycopg2'}  # this is required, or South breaks

models.py:

from django.db import connection

@shared_task
def my_task(track):
    print 'in the task', track.id, track.points.all().count()

def some_method():
    t = Track()
    t.save()
    t = fill_with_points(t)  # creating points, attaching them to a Track
    t.save()
    print 'before the task', track.id, track.points.all().count()
    connection.on_commit(lambda: my_task.delay(t))

结果:

before the task, 21346, 2971
in the task, 21346, 2971

这种常见的用例没有本地芹菜或Django解决方案,这似乎仍然很奇怪。