2012-10-25 56 views
2

我有一個應用程序是一個任務管理器。Rails 3.2 ActiveRecord併發性

每個用戶都可以選擇一個新任務分配給自己。

如果2個用戶在同一時間接受相同的任務,是否存在併發問題?

我的代碼如下所示:

if @user.task == nil 
    @task.user = @user 
    @task.save 
end 

如果2 diferent用戶,2臺diferent機器同時打開這個網址。我有問題嗎?

回答

4

您可以使用optimistic locking來防止其他「過時」記錄被保存到數據庫中。要啓用它,您的模型需要有lock_version列,其默認值爲0

當從數據庫中提取記錄時,當前的lock_version隨之而來。當記錄被修改並保存到數據庫時,數據庫行會有條件地更新,方法是限制lock_version上的UPDATE,該記錄是在提取記錄時出現的。如果它沒有改變,UPDATE將增加lock_version。如果更改,更新將不會執行任何操作,並且會引發異常(ActiveRecord::StaleObjectError)。這是ActiveRecord的默認行爲,除非關閉如下:

ActiveRecord::Base.lock_optimistically = false 

您可以(可選)使用比lock_version其他列名。要使用自定義名稱,添加一行類似下面的模型類:

set_locking_column :some_column_name 

樂觀鎖的替代方案是pessimistic locking,這在數據庫級別依賴於表 - 或者行級鎖。該機制將阻止對鎖定行的所有訪問,從而可能會對性能產生負面影響。

+1

這個什麼:http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html我仍然不明白,但我認爲這是我需要的 –

+0

「樂觀鎖定」(根據您的鏈接)就是上面所描述的答案。 –

4

從來沒有嘗試過,但你可以使用http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html

您應該能夠獲取鎖定您的具體任務,這樣的事情:

@task = Task.find(some_id) 

@task.with_lock do 

    #Then let's check if there's still no one assigned to this task 
    if @task.user.nil? && @user.task.nil? 
    @task.user = @user 
    @task.save 
    end 
end 

再次,我從來沒有使用過這個,所以我想在鎖內測試一個大的sleep以確保它實際上鎖定了所有你想要的方式

另外我不確定reload這裏。由於該行被鎖定,因此可能會失敗。但是在獲得鎖之後,你必須確保你的對象是來自db的新對象,可能有另一種方法來實現它。

編輯:無需重新加載,我檢查了源代碼和with_lock是否適合你。 https://github.com/rails/rails/blob/4c5b73fef8a41bd2bd8435fa4b00f7c40b721650/activerecord/lib/active_record/locking/pessimistic.rb#L61

相關問題