2016-11-14 8 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!返すゼロで見つけるとActiveRecord::RecordNotUniqueエラーが発生create!をトリガします。レスキューブロックはそれをキャッチし、一意でないエラーを引き起こしたレコードを見つけようとするが、nilも返す。私の期待は、レコードがテーブルにコミットされるよりもインデックスの一意性制約が違反された場合です。私は何が欠けていますか?

答えて

0

私自身の質問に答えるには、トランザクション分離レベルを調べてMySQLを探してください。問題のあるコードをトランザクションブロックにラップし、分離レベルを変更します。

オプションは次のとおりです。

  1. READ COMMITTED

    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

UNCOMMITTED
  • SERIALIZABLE
  • REPEATABLE READ READ