使用Rails& amp;数据库锁定不能正常工作Postgres的

时间:2012-07-03 14:39:15

标签: ruby-on-rails ruby postgresql transactions locking

我在rails模型中有以下代码:

foo = Food.find(...)
foo.with_lock do
  if bar = foo.bars.find_by_stuff(stuff)
    # do something with bar
  else
    bar = foo.bars.create!
    # do something with bar
  end
end

目标是确保创建的类型的Bar不会被创建两次。

在控制台上测试with_lock确认了我的期望。但是,在生产中,似乎在某些或所有情况下锁都没有按预期工作,并且正在尝试冗余Bar - 因此,with_lock不会(总是?)导致代码等待轮到

这里可能会发生什么?

更新 很抱歉所有说“锁定foo不会帮助你”的人!我的例子最初没有查找条形码。现在已经修好了。

4 个答案:

答案 0 :(得分:6)

你对with_lock的作用感到困惑。来自fine manual

  

with_lock(lock = true)

     

包装事务中传递的块,在屈服之前锁定对象。您可以将SQL锁定子句作为参数传递(请参阅lock!)。

如果您检查with_lock内部的内容,您会发现它只是lock!周围的薄包装:

  

锁定!(锁定= true)

     

获取此记录的行锁定。重新加载记录以获取请求的锁定。

所以with_lock只是在进行行锁并锁定foo的行。

不要打扰所有这些锁定废话。处理这种情况的唯一理智方法是在数据库中使用唯一约束,除了数据库之外没有人可以确保唯一性,除非你想做一些荒谬的事情,比如锁定整个表;然后继续并盲目地尝试INSERT或UPDATE并陷阱并忽略违反唯一约束时将引发的异常。

答案 1 :(得分:2)

为什么不使用唯一约束?它是为了独特而制作的

答案 2 :(得分:2)

处理这种情况的正确方法实际上在Rails文档中是正确的:

http://apidock.com/rails/v4.0.2/ActiveRecord/Relation/find_or_create_by

begin
  CreditAccount.find_or_create_by(user_id: user.id)
rescue ActiveRecord::RecordNotUnique
  retry
end

(" find_or_create_by"不是原子的,它实际上是一个查找,然后是一个创建。所以用你的find替换它然后创建。这个页面上的文档完全描述了这个案例。)

答案 3 :(得分:1)

锁定在查询缓存中的Rails应用程序中无效的原因。

如果您尝试在单个请求中多次在同一行上获取独占锁,则查询缓存会启动,因此后续锁定查询永远不会到达数据库本身。

Github上的

The issue has been reported