不寻常的ActiveRecord行为

不寻常的ActiveRecord行为

问题描述:

在高争用期间,我遇到了在下表中查找/创建特定记录的意外情况。我相信数据库中存在竞争条件。不寻常的ActiveRecord行为

create_table "business_objects", force: :cascade do |t| 
    t.string "obj_id",  limit: 255 
    t.string "obj_type", limit: 255 
    t.datetime "created_at",    precision: 6, null: false 
    end 

    add_index "business_objects", ["obj_type", "obj_id"], name: "index_business_objects_on_obj_type_and_obj_id", unique: true, using: :btree 

违规代码:

def find_or_create_this 
attributes = self.attributes.slice('obj_id', 'obj_type') 
BusinessObject.find_or_create_by!(attributes) 
rescue ActiveRecord::RecordNotUnique 
    BusinessObject.find_by!(attributes) 
end 

的发现在find_or_create_by!返回nil并触发这引起了一个ActiveRecord::RecordNotUnique错误create!。救援块捕获它并试图找到导致不唯一错误的记录,但它也返回nil。我的期望是,如果索引唯一性约束被违反,则应该将该记录提交给表。我错过了什么?

要回答我自己的问题,请研究MySQL的事务隔离级别。将有问题的代码封装在事务块中并修改隔离级别。

的选项有:

  1. 未提交读
  2. SERIALIZABLE
  3. 重复读
  4. 提交读

    def find_or_create_this attributes = self.attributes.slice('obj_id', 'obj_type') ActiveRecord::Base.transaction(isolation: :read_committed) do BusinessObject.find_or_create_by!(attributes) end rescue ActiveRecord::RecordNotUnique ActiveRecord::Base.transaction(isolation: :read_committed) do BusinessObject.find_by!(attributes) end end