I'm trying to battle some race cases with my background task manager. Essentially, I have a Thing
object (already exists) and assign it some properties, and then save it. After it is saved with the new properties, I queue it in Resque, passing in the ID.
我正在尝试与我的后台任务经理对抗一些种族案件。基本上,我有一个Thing对象(已经存在)并为其分配一些属性,然后保存它。使用新属性保存后,我将其排入Resque,并传入ID。
thing = Thing.find(1)
puts thing.foo # outputs "old value"
thing.foo = "new value"
thing.save
ThingProcessor.queue_job(thing.id)
The background job will load the object from the database using Thing.find(thing_id)
.
后台作业将使用Thing.find(thing_id)从数据库加载对象。
The problem is that we've found Resque is so fast at picking up the job and loading the Thing
object from the ID, that it loads a stale object. So within the job, calling thing.foo
will still return "old value" like 1/100 times (not real data, but it does not happen often).
问题是我们发现Resque在获取作业和从ID加载Thing对象方面非常快,它加载了一个陈旧的对象。因此,在工作中,调用thing.foo仍将返回“旧值”,如1/100次(不是真实数据,但它不会经常发生)。
We know this is a race case, because rails will return from thing.save
before the data has actually been commit to the database (postgresql in this case).
我们知道这是一个竞赛案例,因为在数据实际提交到数据库之前,rails将从thing.save返回(在本例中为postgresql)。
Is there a way in Rails to only execute code AFTER a database action has commit? Essentially I want to make sure that by the time Resque loads the object, it is getting the freshest object. I know this can be achieved using an after_commit
hook on the Thing
model, but I don't want it there. I only need this to happen in this one specific context, not every time the model has commit changed to the DB.
Rails中有一种方法只能在数据库操作提交后执行代码吗?基本上我想确保在Resque加载对象时,它会得到最新鲜的对象。我知道这可以使用Thing模型上的after_commit钩子来实现,但我不想在那里。我只需要在这个特定的上下文中发生这种情况,而不是每次模型都提交更改为DB。
2 个解决方案
#1
5
You can put in a transaction as well. Just like the example below:
您也可以进行交易。就像下面的例子:
transaction do
thing = Thing.find(1)
puts thing.foo # outputs "old value"
thing.foo = "new value"
thing.save
end
ThingProcessor.queue_job(thing.id)
Update: there is a gem which calls After Transaction, with this you may solve your problem. Here is the link: http://xtargets.com/2012/03/08/understanding-and-solving-race-conditions-with-ruby-rails-and-background-workers/
更新:有一个调用After Transaction的gem,你可以解决这个问题。这是链接:http://xtargets.com/2012/03/08/understanding-and-solving-race-conditions-with-ruby-rails-and-background-workers/
#2
0
What about wrapping a try
around the transaction
so that the job is queued up only upon success of the transaction?
如果围绕事务进行尝试,那么只有在事务成功时才将作业排队?
#1
5
You can put in a transaction as well. Just like the example below:
您也可以进行交易。就像下面的例子:
transaction do
thing = Thing.find(1)
puts thing.foo # outputs "old value"
thing.foo = "new value"
thing.save
end
ThingProcessor.queue_job(thing.id)
Update: there is a gem which calls After Transaction, with this you may solve your problem. Here is the link: http://xtargets.com/2012/03/08/understanding-and-solving-race-conditions-with-ruby-rails-and-background-workers/
更新:有一个调用After Transaction的gem,你可以解决这个问题。这是链接:http://xtargets.com/2012/03/08/understanding-and-solving-race-conditions-with-ruby-rails-and-background-workers/
#2
0
What about wrapping a try
around the transaction
so that the job is queued up only upon success of the transaction?
如果围绕事务进行尝试,那么只有在事务成功时才将作业排队?