2016-08-02 78 views
0

我有一個表,我需要選擇一些行並根據以前的值進行更新,或者如果不存在則創建它,並且如果創建了多個HTTP請求,我需要這樣做是安全的一次關於同一行/記錄。Rails SQlite3 ActiveRecord選擇然後更新

我嘗試使用事務和ActiveRecord.lock,但它導致「ActiveRecord :: StatementInvalid(SQLite3 :: BusyException:數據庫被鎖定」,只要有多個請求。我的意圖是,第二個請求只會等待/阻止SELECT語句和活動記錄將使用在DB提供的設施,最有效的方式這樣做(例如,行鎖定(如果可用),等等)。

MyModel.transaction 
    record = MyModel.lock.find_by(name: update_name) #name is a unique index in the SQL table 
    if record 
    # The actual manipulation is more complex than something I want to do directly in a SQL statement 
    record.update!(value: record.value + add_value) 
    else 
    record.create(name: update_name, value: add_value) 
    end 
end 

目前使用Rails 5使用Ruby 2.3。 1

回答

0

這是一個數據庫問題,你需要做兩件事:

如果您還沒有數據庫,請將您的name設爲唯一,如果您嘗試使用相同的name保存新記錄,則數據庫將失敗,您需要相應地處理該錯誤:告訴用戶失敗,該名稱已被選中,而不是。

對於創建或更新,你可以使用:

MyModel.find_or_create_by(name: update_name) do |record| 
    record.value += add_value 
    record.save 
end 

但同樣,這種方法上面不是原子的,這就是爲什麼你必須包裝一個rescue周圍的一切,並相應地處理錯誤。

+0

我說這個列是db中的一個唯一索引,所以如果我真的不得不手動處理約束異常,我可以,而不是一個自動化的解決方案。但是選擇和更新之間的競賽會有效地破壞我的數據(最後一次更新「勝利」,但它存儲的值是錯誤的),並且在那裏沒有唯一的索引違規,所以沒有例外/警告來解救? –