Rails中的竞争条件first_or_create

Mar*_*ska 15 activerecord ruby-on-rails ruby-on-rails-3

我正试图在我的一个表字段中强制执行值的唯一性.更改表格不是一种选择.我需要使用ActiveRecord有条件地在表中插入一行,但我担心同步.

first_or_create在Rails ActiveRecord中是否可以防止竞争条件?

这是first_or_create来自GitHub 的源代码:

def first_or_create(attributes = nil, options = {}, &block)
  first || create(attributes, options, &block)
end
Run Code Online (Sandbox Code Playgroud)

由于多个进程的同步问题,重复条目是否可能导致数据库?

Chr*_*ers 20

Rails 4 文档find_or_create_by提供了一个可能对此情况有用的提示:

请注意,此方法不是原子方法,它首先运行SELECT,如果没有结果,则尝试INSERT.如果存在其他线程或进程,则两个调用之间存在竞争条件,并且最终可能会出现两个类似的记录.

这是否是一个问题取决于应用程序的逻辑,但在行具有UNIQUE约束的特定情况下,可能会引发异常,只需重试:

begin
  CreditAccount.find_or_create_by(user_id: user.id)
rescue ActiveRecord::RecordNotUnique
  retry
end
Run Code Online (Sandbox Code Playgroud)

类似的错误捕获可能对Rails 3有用.(不确定是否ActiveRecord::RecordNotUnique在Rails 3中抛出相同的错误,因此您的实现可能需要不同.)

  • 不,"重试"每次在块之前出现错误时都会重试该块.如果你想避免进入循环,你需要计算重试的次数,如果`tries> max_tries`你不能重试(但要加注).看看`retryable`宝石. (5认同)

Dav*_*vid 5

是的,这是可能的.

您可以显着降低与乐观悲观锁定发生冲突的可能性.当然,乐观锁定需要在表中添加一个字段,而悲观锁定也不能扩展 - 此外,它还取决于您的数据存储的功能.

我不确定你是否需要额外的保护,但它是可用的.

  • 他们遭受同样的竞争条件 (2认同)