2012-10-12 52 views
0

我有一個應用程序,其中人們註冊的項目。每個項目的插槽數量有限。我該如何處理併發?我試過這樣的項目類:併發關聯在ActiveRecord中

def sign_up(signup) 
    ActiveRecord::Base.transaction do 
    return 'Sorry, that item is full.' if full? 
    signups << signup 
    sheet.save! 
    nil 
    end 
end 

def full? 
    locked_signups = signups.lock(true).all 
    locked_signups.size >= max_signups 
end 

是我試圖通過AR甚至可能做什麼?我是否需要通過列實現自己的鎖定?歡迎任何建議。

更新:我得到了這個工作每塔德曼的答案。以下是可用的代碼:

rows_updated = ActiveRecord::Base.transaction do 
    Item.connection.update "update items set signup_count=signup_count+1 where id=#{ActiveRecord::Base.sanitize(self.id)} and signup_count<quantity" 
end 
return 'Sorry, that item is full. Refresh the page to see what\'s still open.' if rows_updated < 1 

回答

1

我可以想到兩種解決這類問題的方法。

計數器列

您將創建一個「剩餘庫存」列並自動更新:

UPDATE sheet SET signups_remaining=signups_remaining-:count WHERE id=:id AND signups_remaining>=:count 

你就不得不相應地綁定到:count:id值。如果此查詢運行,則意味着有足夠的註冊數量。

保留註冊等

創建提前註冊記錄,並對其進行分配:

UPDATE signups SET allocation_id=:allocation_id WHERE allocation_id IS NULL LIMIT :count 

這將更新零個或多個註冊記錄,所以你必須要檢查是否保留正確的在提交您的交易之前進行計數。

+0

使用任一選項,我們不會有同樣的併發問題嗎?第二個(第三,第四等)用戶在先前用戶聲明一個插槽(通過插入一個新註冊行)之後並且在提交signups_remaining更新之前讀取signups_remaining值。 –

+0

這兩者都是可靠的,因爲在第一種情況下,如果庫存不足,查詢將不會返回執行結果,而在第二種情況下,您只會聲明與當前未分配一樣多的查詢。 'WHERE allocation_id IS NULL'確保它不會覆蓋以前的任何工作。這是可能的,因爲這些都是單原子語句,既可以運行也可以不運行。兩個單獨的查詢更難同步。 – tadman