在更新数据库提交后运行rails代码,没有after_commit

时间:2022-12-18 01:19:32

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?

如果围绕事务进行尝试,那么只有在事务成功时才将作业排队?