5

どういうわけか、私はいつも金曜日にこれらを手に入れます。

以前の質問は同じ問題に関するものでしたが、今では少し絞り込むことができます。

私はこれを理解しようと一日中遊んでいます。次のように指定されたlock_version列を持つテーブルがあります。

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

そして、私はこのようなことをします:

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

次に、1番目と2番目が同じオブジェクトを参照していることを確認します。それらの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

...そして何も起こりません。正しい(スローされた例外)動作が得られるのは、最初と2番目のlock_versionが0の場合だけです。ただし、最初の保存後は、再び0になることはありません。いったいこれで何が起きているのでしょうか?

私はruby1.8.6とアクティブレコード2.2.2を使用しています

ありがとう...

4

3 に答える 3

6

first.save を 2 回目に呼び出すと、some_attribute_field の値がすでに「first」に等しい場合、activerecord はこれを認識しているため、db で更新されず、lock_version がインクリメントされません。2番目の保存は、データベースが「最初」で変更されていないため機能します。

2 番目のテストの値を "first" 以外の値に変更して、db の値と異なるようにしてください。

于 2009-05-07T13:11:02.033 に答える
2

私は Ruby の専門家ではありませんが、楽観的ロックはよく知っているので、デバッグの手助けをしようと思います。

2 回目の保存で実際にデータベースが更新されると思います。両方のオブジェクトの lock_version が異なり、かつ lock_version が UPDATE で使用されている場合、それは不可能です (UPDATE はゼロ行を更新します)。したがって、2 つの選択肢しかありません。

  • lock_version が UPDATE ステートメントで使用されていないか、正しく使用されていません
  • 両方のオブジェクトが何らかの方法で同じ lock_version を取得しました

(実際には、3 番目の選択肢があります。両方の save() は独自のトランザクションにありますが、 AUTOCOMMIT=true があると思います)

実際の SQL ステートメントを可視化できますか? 更新ステートメントは次のようになります

... WHERE JOB_ID=123 AND LOCK_VERSION=8

実際のクエリを手元に用意すると、何が起こっているのかを理解するのがはるかに簡単になります。

PSともう1つ:別のトピックの例では、このオブジェクトがあります:

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

プロパティがロード時間と比較して変更されていない場合、save() 呼び出しはコンテナーによって無視される場合があります。ただし、ActiveRecord にこの最適化があるかどうかはわかりません。しかし、その場合、2 番目の save() は無視され、楽観的ロックが開始される機会がありません。

于 2009-05-04T17:20:25.953 に答える
1

Vladimir が言ったように、テスト/サンプル コードには少し欠陥があります。属性が変更されていないため、最初は 2 番目の 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 バージョンで例を使用すると、秒が保存されたときに古いオブジェクトの例外が発生するはずです (両方の場合!)

于 2009-05-10T14:10:11.820 に答える