Почему оптимистическая блокировка activerecord работает только один раз на строку? - PullRequest
5 голосов
/ 02 мая 2009

Почему-то я всегда получаю их по пятницам.

Мой предыдущий вопрос касался той же проблемы, но теперь я могу немного сузить круг вопросов:

Я играл с этим весь день, пытаясь понять это. У меня есть таблица со столбцом lock_version, указанная таким образом:

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

И я делаю что-то вроде этого:

foo = job.create!
first = Job.find(foo.id)
second = Job.find(foo.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 и активную запись 2.2.2

Спасибо ... * * 1023

Ответы [ 3 ]

6 голосов
/ 07 мая 2009

когда вы вызываете first.save во второй раз, значение some_attribute_field уже равно «first», activerecord знает об этом, поэтому не обновляется в БД, чтобы lock_version не увеличивалась. Второе сохранение работает, так как база данных никогда не изменялась с «первым».

Попробуйте изменить значение во втором тесте на значение, отличное от «first», чтобы оно отличалось от значения в БД.

2 голосов
/ 04 мая 2009

Я не Ruby, но оптимистическая блокировка мне знакома, поэтому я постараюсь помочь вам отладить ее.

Полагаю, второе сохранение фактически обновляет базу данных. Если оба объекта имеют разную lock_version и Lock_version используется в UPDATE, это просто невозможно (UPDATE обновит ноль строк). Итак, у нас есть только две альтернативы:

  • lock_version не используется в операторе UPDATE или используется неправильно
  • оба объекта получили один и тот же lock_version

( на самом деле, существует третий вариант: оба save () находятся в своей собственной транзакции, но я чувствую, что у вас AUTOCOMMIT = true )

Можете ли вы сделать фактические операторы SQL видимыми? Оператор обновления должен читать что-то вроде

... WHERE JOB_ID=123 AND LOCK_VERSION=8

Когда у вас будут на самом деле под рукой запросы, будет гораздо проще понять, что происходит.

P.S. и еще один: в вашем примере в другой теме у вас есть этот объект:

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

Вызов save () может игнорироваться контейнером, если свойства не изменены по сравнению со временем загрузки. Я не знаю, если ActiveRecord имеет эту оптимизацию или нет. Но если это произойдет, то второе save () будет проигнорировано, и оптимистическая блокировка не сможет сработать.

1 голос
/ 10 мая 2009

Как сказал Владимир, ваш код теста / примера немного ошибочен. Первый не сохраняется в базе данных во время второго сохранения! (), Потому что никакие атрибуты не изменились. Смотрите следующий пример:

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 при сохранении секунды (оба раза!)

...