2

我有一个应用程序是任务管理器。

每个用户都可以选择分配给自己的新任务。

如果 2 个用户同时接受同一个任务,是否存在并发问题?

我的代码如下所示:

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

如果 2 个不同的用户,在 2 个不同的机器上同时打开这个 url。我会有问题吗?

4

2 回答 2

5

您可以使用乐观锁定来防止将其他“陈旧”记录保存到数据库中。要启用它,您的模型需要有lock_version一个默认值为 的列0

当从数据库中获取记录时,电流lock_version随之而来。当记录被修改并保存到数据库时,数据库行会通过限制获取记录时存在的UPDATEon有条件地更新lock_version。如果它没有改变,UPDATE将增加lock_version. 如果它已更改,则更新将不执行任何操作,并且将引发异常 ( ActiveRecord::StaleObjectError)。这是 ActiveRecord 的默认行为,除非如下关闭:

ActiveRecord::Base.lock_optimistically = false

您可以(可选)使用除lock_version. 要使用自定义名称,请在模型类中添加如下所示的行:

set_locking_column :some_column_name

乐观锁定的替代方法是悲观锁定,它依赖于数据库级别的表级或行级锁。此机制将阻止对锁定行的所有访问,因此可能会对您的性能产​​生负面影响。

于 2012-10-25T13:52:29.600 回答
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这里。由于该行被锁定,它可能会失败。但是你必须确保你的对象在获取锁后是从数据库中更新的,可能还有另一种方法可以做到这一点。

编辑:无需重新加载,我检查了源代码并with_lock为您完成。 https://github.com/rails/rails/blob/4c5b73fef8a41bd2bd8435fa4b00f7c40b721650/activerecord/lib/active_record/locking/pessimistic.rb#L61

于 2012-10-25T14:01:34.570 回答