我有一个应用程序是一个任务管理器。Rails 3.2 ActiveRecord并发性
每个用户都可以选择一个新任务分配给自己。
如果2个用户在同一时间接受相同的任务,是否存在并发问题?
我的代码如下所示:
if @user.task == nil
@task.user = @user
@task.save
end
如果2 diferent用户,2台diferent机器同时打开这个网址。我有问题吗?
我有一个应用程序是一个任务管理器。Rails 3.2 ActiveRecord并发性
每个用户都可以选择一个新任务分配给自己。
如果2个用户在同一时间接受相同的任务,是否存在并发问题?
我的代码如下所示:
if @user.task == nil
@task.user = @user
@task.save
end
如果2 diferent用户,2台diferent机器同时打开这个网址。我有问题吗?
您可以使用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,这在数据库级别依赖于表 - 或者行级锁。该机制将阻止对锁定行的所有访问,从而可能会对性能产生负面影响。
从来没有尝试过,但你可以使用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
这个什么:http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html我仍然不明白,但我认为这是我需要的 –
“乐观锁定”(根据您的链接)就是上面所描述的答案。 –