2012-10-25 58 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

相关问题