I have a question regarding transactions and celery tasks. So it's no mystery to me that of course if you have a transaction and a celery task accessing the same table/records we'll have a race condition.
我有一个关于交易和芹菜任务的问题。所以对我来说毫无疑问如果你有一个事务和一个芹菜任务访问相同的表/记录我们就会有一个竞争条件。
However, consider the following piece of code:
但是,请考虑以下代码:
def f(self):
# function of module that inherits from models.Model
self.field_a = datetime.now()
self.save()
transaction.commit_unless_managed()
# depending on the configuration of this module
# this might return None or a datetime object.
eta = self.get_task_eta()
if eta:
celery_task_do_something.apply_async(args=(self.pk, self.__class__),
eta=eta)
else:
celery_task_do_something.delay(self.pk, self.__class__)
Here's the celery task:
这是芹菜的任务:
def celery_task_do_something(pk, cls):
o = cls.objects.get(pk=pk)
if o.field_a:
# perform something
return True
return False
As you can see, before creating the task we call transaction.commit_unless_managed
and it should commit, since django transaction is not currently managed.
如您所见,在创建任务之前,我们调用transaction.commit_unless_managed,并且它应该提交,因为django事务当前没有被管理。
However, when running celery task the field field_a
is not set.
然而,当运行芹菜任务时,字段field_a没有设置。
My question:
我的问题:
Since we do commit before creating the task, is it still possible that there's a race condition?
既然我们在创建任务之前已经提交了,那么是否仍然存在竞争条件呢?
Additional info
额外的信息
-
We're using Postgres version 9.1
我们使用的是Postgres版本9.1
-
Every transaction is run with READ COMMITTED isolation level
每个事务都在读取提交的隔离级别上运行
-
On a different db with engine
dowant.lib.db.backends.postgresql_psycopg2_debugger
field_a
is already set and the task works as expected. With enginedowant.lib.db.backends.postgresql_psycopg2_hstore_ready
the described issue appears (not sure if it's related with the engine).在一个不同的db上,用引擎dowant.lib.db.backends。已经设置了postgresql_psycopg2_debugger field_a,任务按照预期工作。与引擎dowant.lib.db.backends。postgresql_psycopg2_hstore_ready将出现所描述的问题(不确定它是否与引擎相关)。
-
Celery version is 2.2
芹菜的版本是2.2
-
I tried different databases. Still the same behavior, except when the engines change. So that's why I mentioned this.
我试着不同的数据库。除了引擎改变外,其他的行为都是一样的。这就是我提到这个的原因。
Thanks a lot.
非常感谢。
1 个解决方案
#1
1
Try to add self.__class__.objects.select_for_update().get(pk=self.pk)
before save
and see what happens.
尝试在保存之前添加self.__class__.objects.select_for_update().get(pk=self.pk),看看会发生什么。
It should block all reads to this row untill commit is done.
它应该阻塞对这一行的所有读取,直到提交完成。
#1
1
Try to add self.__class__.objects.select_for_update().get(pk=self.pk)
before save
and see what happens.
尝试在保存之前添加self.__class__.objects.select_for_update().get(pk=self.pk),看看会发生什么。
It should block all reads to this row untill commit is done.
它应该阻塞对这一行的所有读取,直到提交完成。