2011-07-20 45 views
1

我有一個非常簡單的Rails 3應用程序,用戶可以在其中爲特定日期保留有限數量的同類項目。我試圖避免兩個人在某一天保留最後一個項目的競賽狀態。該模型(簡化)如下:Rails 3 - 潛在的比賽條件?

class Reservation < ActiveRecord::Base 
    belongs_to :user 
    attr_accessible :date 
    MAX_THINGS_AVAILABLE = 20 

    validate :check_things_available 

    def check_things_available 
    unless things_available? errors[:base] << "No things available" 
    end 

    def things_available? 
    Reservation.find_all_by_date(date).count < MAX_THINGS_AVAILABLE 
    end   
end 

預約正在控制器通過current_user.reservations.build(params[:reservation])

感覺就像有一個更好的方式來做到這一點創建的,但我不能完全把我的指着它是什麼。任何有關如何防止競賽狀況的幫助將不勝感激。

+0

在http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html看一看你提供 – Gazler

+0

鏈接引用更新屬性/列在現有記錄中,並且當創建兩個新記錄時,我的競賽狀況將發生。我錯過了什麼嗎?另外,我不清楚在這種情況下行級鎖定是否會起作用,因爲我使用從所有行的子集派生的計數。 –

+0

我認爲在過濾器之前使用是最簡單的方法。如果它們返回錯誤,則整個交易將被取消。 – Spyros

回答

0

不知道這個回答你的問題,但它可能會爲您指出一個解決方案:

http://webcache.googleusercontent.com/search?q=cache:http://barelyenough.org/blog/2007/11/activerecord-race-conditions/

(原網站似乎是下來,這是一個鏈接到谷歌緩存)

該頁面上的結論是,樂觀鎖定和行級別鎖定不是創建競爭條件的解決方案,而僅僅是更新。

作者建議用db約束來重新實現find_or_create。

另一個建議是,將事務隔離級別切換到「可序列化」應該可以工作,但是沒有關於如何在Rails中執行此操作的信息。

0

只要使用任何鎖定機制像redis locker

RedisLocker.new("thing_to_sell_#{@thing.id}").run do 
    current_user.create_reservation(@thing) or raise "Item already sold to another user" 
end