11

我正在尝试与我的后台任务管理器对抗一些比赛案例。本质上,我有一个Thing对象(已经存在)并为其分配了一些属性,然后保存它。在它与新属性一起保存后,我在 Resque 中对其进行排队,并传入 ID。

thing = Thing.find(1)
puts thing.foo # outputs "old value"
thing.foo = "new value"
thing.save
ThingProcessor.queue_job(thing.id)

后台作业将使用Thing.find(thing_id).

问题是我们发现 Resque 在获取工作和Thing从 ID 加载对象方面非常快,以至于它加载了一个陈旧的对象。所以在工作中,调用thing.foo仍然会返回 1/100 倍的“旧值”(不是真实数据,但它并不经常发生)。

我们知道这是一个竞争案例,因为 rails 会thing.save 在数据实际提交到数据库之前返回(在这种情况下是 postgresql)。

Rails 有没有办法只在数据库操作提交后执行代码?本质上,我想确保在 Resque 加载对象时,它正在获取最新的对象。我知道这可以使用模型after_commit上的钩子来实现Thing,但我不希望它在那里。我只需要在这个特定的上下文中发生这种情况,而不是每次模型提交更改到数据库时。

4

3 回答 3

4

您也可以进行交易。就像下面的例子:

transaction do
  thing = Thing.find(1)
  puts thing.foo # outputs "old value"
  thing.foo = "new value"
  thing.save
end
ThingProcessor.queue_job(thing.id)

更新:有一个 gem 调用 After Transaction,这样你可以解决你的问题。这是链接:http: //xtargets.com/2012/03/08/understanding-and-solving-race-conditions-with-ruby-rails-and-background-workers/

于 2013-08-16T19:08:11.310 回答
0

将 a 包裹try起来以transaction使作业仅在事务成功后才排队呢?

于 2013-08-17T12:18:17.790 回答
0

我有一个类似的问题,我需要在运行一系列操作之前确保事务已经提交。我最终使用了这个宝石:

https://github.com/Envek/after_commit_everywhere

这意味着我可以执行以下操作:

def finalize!
  Order.transaction do
    payment.charge!

    # ...

    # Ensure that we only send out items and perform after actions when the order has defintely be completed
    after_commit { OrderAfterFinalizerJob.perform_later(self) }
  end
end
于 2019-06-12T12:23:45.963 回答