ActiveRecord 낙관적 잠금이 행 당 한 번만 작동하는 이유는 무엇입니까?
-
03-07-2019 - |
문제
어떻게 든, 나는 항상 금요일에 이것을 얻습니다.
나의 초기 질문은 같은 문제에 관한 것이었지만 이제는 조금 좁힐 수 있습니다.
나는 하루 종일 놀면서 그것을 이해하려고 노력했다. leop_version colum이있는 테이블이 있습니다.
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
... 그리고 아무 일도 일어나지 않습니다. 내가 올바른 (예외를 던진) 동작을 얻는 유일한 시간은 첫 번째와 두 번째가 0의 잠금 장치를 가질 때입니다. 그러나 첫 번째 저장 후에는 다시는 0이 아닙니다. 이 지구상에서 무슨 일이 일어나고 있습니까?
Ruby 1.8.6과 Active Record 2.2.2를 사용하고 있습니다
감사...
해결책
두 번째로 전화하면 some_attribute_field의 값은 이미 "첫 번째"와 동일합니다. ActiveRecord는 이것을 알고 있으므로 DB에서 leop_version으로 업데이트되지 않으므로 증가하지 않습니다. 두 번째 저장은 DB가 "첫 번째"로 변경되지 않았기 때문에 작동합니다.
두 번째 테스트의 값을 DB에있는 것과 다르기 위해 "첫 번째"이외의 다른 것으로 변경하십시오.
다른 팁
나는 루비 남자는 아니지만 낙관적 잠금은 나에게 친숙하므로 디버깅을 도와 줄게.
두 번째 저장은 실제로 데이터베이스를 업데이트한다고 가정합니다. 두 객체의 잠금 장치가 다르고 leop_version이 업데이트에 사용되면 단순히 불가능합니다 (업데이트는 0 행을 업데이트합니다). 따라서 두 가지 대안 만 있습니다.
- Lock_version은 업데이트 문에 사용되지 않거나 잘못 사용됩니다.
- 두 개체 모두 어떻게 든 동일한 잠금 장치를 얻었습니다
(실제로 세 번째 대안이 있습니다. Save ()는 자체 거래에 있지만 AutoCommit = True가 있다고 생각합니다.)
실제 SQL 문을 보이게 할 수 있습니까? 업데이트 명령문은 같은 것을 읽어야합니다
... WHERE JOB_ID=123 AND LOCK_VERSION=8
실제로 쿼리를 할 때 무슨 일이 일어나고 있는지 이해하는 것이 훨씬 쉬울 것입니다.
추신 및 한 가지 더 : 다른 주제의 예에서는이 객체가 있습니다.
#<Job id: 323, lock: 8, worker_host: "second">
로드 시간과 비교하여 속성이 변경되지 않으면 컨테이너로 저장 () 호출을 무시할 수 있습니다. ActiveRecord 에이 최적화가 있는지 여부는 모르겠습니다. 그러나 그렇다면 두 번째 저장 ()가 무시되고 낙관적 잠금은 시작될 기회가 없습니다.
Vladimir가 말했듯이 테스트/예제 코드는 약간 결함이 있습니다. 먼저 속성이 변경되지 않았기 때문에 두 번째 저장 중에 DateBase에 저장되지 않습니다! (). 다음 예를 참조하십시오.
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에서 예제를 사용하면 두 번째 저장이 저장 될 때 오래된 객체 예외가 있어야합니다 (두 번 모두!)