2016-11-14 6 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 и запускает create!, который вызывает ошибку ActiveRecord::RecordNotUnique. Блок спасения улавливает его и пытается найти запись, которая вызвала не уникальную ошибку, но также возвращает ноль. Мое предположение заключается в том, что если ограничение уникальности индекса нарушено, то запись должна быть привязана к таблице. Что мне не хватает?

ответ

0

Чтобы ответить на мой вопрос, изучите уровень изоляции транзакций для MySQL. Оберните код нарушения в блок транзакций и измените уровень изоляции.

Варианты:

  1. READ UNCOMMITTED
  2. SERIALIZABLE
  3. REPEATABLE READ
  4. 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