如何为操作锁定一组对象?

时间:2011-03-24 19:13:34

标签: ruby-on-rails activerecord transactions locking

在我的rails应用程序中,我有一些这样的代码:

def foo
  if object_bar_exists
    raise "can't create bar twice!"
  end

  Bar.create
end

可以通过进入应用程序服务器的两个不同请求调用。如果此代码同时由两个请求运行,并且它们同时运行if检查,则不会找到另一个bar,并且将创建2个bar。< / p>

为“酒吧集合”创建“互斥体”的最佳方法是什么? DB中的专用互斥表?

更新

我应该强调一下,我不能在这里使用内存互斥,因为并发是跨请求/进程而不是线程。

3 个答案:

答案 0 :(得分:6)

最好的办法是在数据库事务中执行操作。因为您最终可能会运行多个应用程序并且它们很可能不会共享内存,所以您将无法在应用程序级别创建Mutex锁定,尤其是当这两个应用程序服务在完全不同的物理机箱上运行时。以下是如何完成数据库事务:

ActiveRecord::Base.transaction do
  # Transaction code goes here.
end

如果要确保数据库事务的回滚,那么您必须在Bar类上启用验证,以便无效的保存请求将导致回滚:

ActiveRecord::Base.transaction do
  bar = Bar.new(params[:bar])
  bar.save!
end

如果您在数据库中已有一个条形对象,则可以像这样悲观地锁定该对象:

ActiveRecord::Base.transaction do
  bar = Bar.find(1, :lock => true)
  # perform operations on bar
end

答案 1 :(得分:1)

如果所有请求都进入同一台机器和同一个ruby虚拟机,则可以使用Ruby内置的Mutex类:Mutex Docs

如果有多台机器或rvms,则必须使用数据库事务来创建/获取Bar对象,假设它以某种方式存储在db中。

答案 2 :(得分:0)

我可能会在lib目录中create a Singleton对象,因此您只有一个实例,并使用Mutexes来锁定它。

这样,您可以确保在任何给定时间点只能访问该事物。当然,任何其他请求都会阻止它,所以要记住这一点。

对于多台计算机,您必须将令牌存储在数据库中,并同步对令牌的某种访问。例如,必须查询和提取令牌,并跟踪某些数字或某些内容以确保人们无法同时删除令牌。或者使用集中锁定Web服务,以便您的令牌处理仅在一个位置。