find_or_create ActiveRecord :: RecordNotUnique rescue retry无法正常工作

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了find_or_create ActiveRecord :: RecordNotUnique rescue retry无法正常工作相关的知识,希望对你有一定的参考价值。

使用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

`

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

有任何想法吗?

谢谢!

答案

如果你正在使用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。

以上是关于find_or_create ActiveRecord :: RecordNotUnique rescue retry无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章

是否有多个验证存在

Rails 还是 Grails? [关闭]

Rails 使用别名查询连接关联表

使用 Rails 6 ActiveRecord 进行完全外连接

Rails为全球化翻译定义数据库

codeigniter 查询生成器和活动记录 sql 注入