find_or_create ActiveRecord :: RecordNotUnique rescue retry not working

时间:2016-12-09 00:47:06

标签: ruby-on-rails ruby activerecord

使用ruby 2.1.2和rails 4.1.4。我对数据库中的电子邮件列有一个独特的约束。如果存在竞争条件,其中一个线程正在创建用户而另一个线程同时尝试创建记录而不是唯一异常。我试图通过重试来处理此异常,以便在重试时它将在find_or_create中的SELECT期间找到客户。

然而,它似乎没有起作用。它没有重试,然后重新引发异常。

这是我最初在做的事情:

retries = 10
begin
  user = User.find_or_create_by(:email => email)
rescue ActiveRecord::RecordNotUnique
  retry unless (retries-=1).zero? 
  raise   
end

然后我想也许数据库连接正在缓存SELECT查询结果,导致它认为用户仍然不存在并继续尝试创建它。为了解决这个问题,我尝试使用Model.uncached来禁用查询缓存:

retries = 10
begin
  User.uncached do
    user = User.find_or_create_by(:email => email)
  end
rescue ActiveRecord::RecordNotUnique
  retry unless (retries-=1).zero? 
  raise   
end

`

这也行不通。我不知道还有什么办法可以解决这个问题?我应该增加重试吗?在重试之间添加睡眠延迟?有没有更好的方法来清除查询缓存(如果这是问题?)

有什么想法吗?

谢谢!

1 个答案:

答案 0 :(得分:0)

如果您正在使用Postgres,则会有一个upsert(https://github.com/jesjos/active_record_upsert)gem,这样可以很容易地在这样的场景中添加“ON CONFLICT DO NOTHING / UPDATE”。您可以使用

替换代码
User.upsert(email: email)

如果要在同一操作中添加或更新其他数据作为同一upsert调用的一部分,则应首先指定该电子邮件是该模型的唯一键:

class User < ActiveRecord::Base
  upsert_keys [:email]
end

编辑:要确定您的方法的问题,请尝试发布日志的相关部分,我们可以在其中查看正在发布的SQL语句,包括事务BEGIN / COMMIT。