如何避免Race Condition和Lock等待超时更新页面的视图

时间:2015-03-10 08:45:38

标签: ruby-on-rails ruby race-condition

在Rails应用程序中,用户访问我显示弹出窗口的页面。 我想在每次用户看到弹出窗口时更新记录。

为避免竞争条件,我使用乐观锁定(因此我在弹出表中添加了一个名为lock_version的字段)。

代码很简单:

# inside pages/show.html.erb
<%= render @popup %>

# and inside the popup partial...
...
<%
  Popup.transaction do
     begin
       popup.update_attributes(:views => popup.views + 1)
     rescue ActiveRecord::StaleObjectError
       retry
     end
  end
%>

问题是很多用户访问该页面,并且mysql超过锁定超时。

所以网站冻结了,我收到了很多这样的错误: 超过锁定等待超时;尝试重新启动交易

那是因为有很多未决请求尝试使用过时的lock_version值更新记录。

如何解决我的问题?

2 个答案:

答案 0 :(得分:0)

您可以使用increment_counter,因为它会生成一个SQL UPDATE查询而不会锁定。

但我认为在你的情况下使用像Redis这样的任何键值数据库来存储和更新你的弹出计数器会更好,因为它可以比SQL DB更快地完成它。

答案 1 :(得分:0)

如果你不能采用像他们的回复中提到的@maxd这样的方法,你可以利用Sidekiq之类的异步库来处理这些类型的请求(其中他们只需要在工作队列)。

<强> LIB / some_made_up_class.rb

def increment_popup(popup)
  Popup.transaction do
    begin
       popup.update_attributes(:views => popup.views + 1)
     rescue ActiveRecord::StaleObjectError
       retry
     end
  end
end

然后,在另一段代码中(您的控制器,服务或视图(在视图层中放置逻辑不太理想)。

SomeMadeUpClass.delay.increment_popup(popup)
# OR you can schedule it
SomeMadeUpClass.delay_for(1.second).increment_popup(popup)

这实际上会影响您的插页排队,同时释放您的页面,理论上可以帮助减少您的点击时间等等。

虽然它不仅仅是添加像Sidekiq这样的库(gem)和我在这里的示例代码,但我认为异步库/工具将有很大帮助。