不知何故,我总是在周五收到这些。

我之前的问题是关于同样的问题,但我现在可以缩小范围:

我一整天都在玩这个,试图弄清楚它的意义。我有一个带有 lock_version 列的表,如此指定:

add_column :jobs, :lock_version, :integer, :default=>0

我做了这样的事情:

foo = job.create!
first = Job.find(foo.id)
second = Job.find(foo.id)

然后,我验证第一个和第二个引用同一个对象 - 它们的 id 相同,并且我使用 mysql 命令行工具在数据库中看到该行。

first.some_attribute_field = 'first'
second.some_attribute_field = 'second'
first.save
second.save

到目前为止没有问题。我正确地得到了 ActiveRecord::StaleObjectError 异常。 然而:

first = Job.find(foo.id)
second = Job.find(foo.id)
first.some_attribute_field = 'first'
second.some_attribute_field = 'second'
first.save
second.save

......什么也没有发生。事实证明,我获得正确(抛出异常)行为的唯一一次是当第一个和第二个的 lock_version 为 0 时。不过,第一次保存后,它就再也不会是 0 了。这到底是怎么回事?

我正在使用 ruby​​ 1.8.6 和 active record 2.2.2

谢谢...

有帮助吗?

解决方案

当你第一次调用some.sat第二次时,some_attribute_field的值已经等于“first”,activerecord知道这一点,所以它不会在db中更新到lock_version不会增加。第二次保存工作,因为db从未使用“first”更改。

尝试将第二个测试中的值更改为“第一个”以外的值。所以它与db中的内容不同。

其他提示

我不是 Ruby 爱好者,但乐观锁定对我来说很熟悉,所以我会尽力帮助您调试它。

我认为第二次保存实际上更新了数据库。如果两个对象具有不同的 lock_version 并且在 UPDATE 中使用 lock_version ,则这是不可能的(UPDATE 将更新零行)。所以,我们只有两个选择:

  • lock_version 未在 UPDATE 语句中使用或使用不正确
  • 两个对象以某种方式获得了相同的 lock_version

(实际上,还有第三种选择:两个 save() 都在自己的事务中,但我觉得你有 AUTOCOMMIT=true)

你能让实际的 SQL 语句可见吗?更新语句应该是这样的

... WHERE JOB_ID=123 AND LOCK_VERSION=8

当您手头有实际的查询时,就会更容易理解正在发生的事情。

附:还有一个:在另一个主题的示例中,您有这个对象:

#<Job id: 323, lock: 8, worker_host: "second">

如果属性与加载时间相比没有改变,则 save() 调用可能会被容器忽略。我不知道ActiveRecord是否有这个优化。但如果确实如此,那么第二个 save() 将被忽略,并且乐观锁定没有机会启动。

正如弗拉基米尔所说,你的测试/示例代码有点缺陷。在第二次保存期间,首先不会存储在日期库中!()因为没有更改属性。请参阅以下示例:

foo = Account.create!

first = Account.find(foo.id)
first.cash = 100
first.save!


first = Account.find(foo.id)
first.cash = 100

puts "First lock before " + first.lock_version.to_s
first.save!
puts "First lock after " + first.lock_version.to_s

这会产生:

% script/runner another_tester.rb                               
First lock before 1
First lock after 1

使用2.3.2版本的rails中的示例,当第二个被保存时(两次都是!),它应该与Stale对象异常一起工作。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top