activerecord楽観的ロックが行ごとに1回しか機能しないのはなぜですか?
-
03-07-2019 - |
質問
どういうわけか、私は常に金曜日にこれらを取得します。
以前の質問は同じ問題に関するものでしたが、今では少し絞り込むことができます:
私は一日中、これを理解しようとしてずっと遊んでいます。次のように指定された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になることはありません。これは一体どういうことですか?
Ruby 1.8.6とアクティブレコード2.2.2を使用しています
ありがとう...
解決
some_attribute_fieldの値が既に「first」に等しい2回目にfirst.saveを呼び出すと、activerecordはこれを認識しているため、lock_versionがインクリメントされないようにdbを更新しません。データベースは" first"で変更されなかったため、2番目の保存は機能します。
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()は無視され、楽観的ロックは作動する機会がありません。
ウラジミールが言ったように、テスト/サンプルコードには少し欠陥があります。最初の属性は変更されていないため、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バージョンのrailsで例を使用すると、2番目が保存されたときにStaleオブジェクト例外が発生するはずです(両方とも!)