2016-11-14 129 views
1

在高爭用期間,我遇到了在下表中查找/創建特定記錄的意外情況。我相信數據庫中存在競爭條件。不尋常的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。我的期望是,如果索引唯一性約束被違反,則應該將該記錄提交給表。我錯過了什麼?

回答

0

要回答我自己的問題,請研究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