我有一个应用程序是任务管理器。
每个用户都可以选择分配给自己的新任务。
如果 2 个用户同时接受同一个任务,是否存在并发问题?
我的代码如下所示:
if @user.task == nil
@task.user = @user
@task.save
end
如果 2 个不同的用户,在 2 个不同的机器上同时打开这个 url。我会有问题吗?
我有一个应用程序是任务管理器。
每个用户都可以选择分配给自己的新任务。
如果 2 个用户同时接受同一个任务,是否存在并发问题?
我的代码如下所示:
if @user.task == nil
@task.user = @user
@task.save
end
如果 2 个不同的用户,在 2 个不同的机器上同时打开这个 url。我会有问题吗?
您可以使用乐观锁定来防止将其他“陈旧”记录保存到数据库中。要启用它,您的模型需要有lock_version
一个默认值为 的列0
。
当从数据库中获取记录时,电流lock_version
随之而来。当记录被修改并保存到数据库时,数据库行会通过限制获取记录时存在的UPDATE
on有条件地更新lock_version
。如果它没有改变,UPDATE
将增加lock_version
. 如果它已更改,则更新将不执行任何操作,并且将引发异常 ( ActiveRecord::StaleObjectError
)。这是 ActiveRecord 的默认行为,除非如下关闭:
ActiveRecord::Base.lock_optimistically = false
您可以(可选)使用除lock_version
. 要使用自定义名称,请在模型类中添加如下所示的行:
set_locking_column :some_column_name
乐观锁定的替代方法是悲观锁定,它依赖于数据库级别的表级或行级锁。此机制将阻止对锁定行的所有访问,因此可能会对您的性能产生负面影响。
从未尝试过,但您可以使用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
这里。由于该行被锁定,它可能会失败。但是你必须确保你的对象在获取锁后是从数据库中更新的,可能还有另一种方法可以做到这一点。
编辑:无需重新加载,我检查了源代码并with_lock
为您完成。
https://github.com/rails/rails/blob/4c5b73fef8a41bd2bd8435fa4b00f7c40b721650/activerecord/lib/active_record/locking/pessimistic.rb#L61