Rails ActiveRecord 唯一性验证仍会引发错误:记录已创建

Rails ActiveRecord Uniqueness Validation Raises Error Still The Record Is Created

提问人:Pranjal Nanavaty 提问时间:7/20/2023 更新时间:7/20/2023 访问量:38

问:

Ruby 版本:3.0.4

Rails 版本:6.1.7.2

我在物料模型中定义了唯一性验证检查,以便每个用户只能唯一地创建一个物料。

# Modal - item.rb

validates :name, uniqueness: { case_sensitive: false, scope: :user_id }
# Controller - items.rb

def create
  item = @user.items.new(params)

  item.save!

  rescue ActiveRecord::RecordInvalid => exception
  .
  .
  .

当我收到来自具有相同项目名称的同一用户的多个并行请求时,会引发 execption:

"exception":"ActiveRecord::RecordInvalid: Name Item names must be unique for a user."

记录也会被创建。

附言:

  1. 当请求不是并行的时,验证会成功工作。
  2. 当我收到 8 个这样的并行请求时。对于第一个(原始)请求,已创建记录。对于接下来的 7 个请求,引发了异常,但也创建了 3 个重复记录。

理想情况下,应该提高期望值。但是不应该创建任何记录。

Ruby-on-Rails Ruby 验证 ActiveRecord

评论

0赞 dbugger 7/20/2023
您需要在数据库中创建一个约束来强制执行唯一性 -- 正如您发现的那样,验证是不够的。
0赞 Pranjal Nanavaty 7/21/2023
是的!我在数据库中添加了复合唯一键约束。困扰我的是,验证提高了预期,但记录仍然被创建。
0赞 smathy 7/21/2023
如果引发异常,则不会创建记录。如果没有看到您用来得出结论的代码/过程,即您在创建 3 条记录时有 8 个并行请求导致 7 个异常,则无法解释这里实际发生了什么。如果您仍然确信发生了这种情况,请创建一个新问题,显示您为创建此明显异常而经历的测试过程。

答:

0赞 Arctodus 7/20/2023 #1

ActiveModel 验证不会处理由并行处理请求引起的争用条件。

一种选择是使用唯一索引在数据库级别解决问题。

add_index :items, :user_id, unique: true

另一种方法是使用咨询锁来确保一次只有一个线程能够执行一个代码块

评论

0赞 Pranjal Nanavaty 7/21/2023
当然,谢谢。是的!我在数据库中添加了复合唯一键约束。困扰我的是,验证提高了预期,但记录仍然被创建。