Как я могу выбрать рельсы для обновления в нескольких строках?

Я не могу понять, как указать рельсам SELECT ... FOR UPDATE в нескольких строках. Покопавшись в консоли, Foo.where(bar: "baz").lock действительно выдает правильный SQL. Но когда я пытаюсь сделать это в транзакции, этот рубиновый код на самом деле не генерирует SQL. Я думаю, что одноразовый вариант работает, потому что консоль rails автоматически запускает .all для отношения для отображения результатов.

Вот что я запускаю в консоли

Foo.transaction do
  Foo.where(bar: "baz").lock  
  Foo.where(bar: "baz").update_all(bar: "baz2")  
end

и SQL:

BEGIN
  Foo Update All (0.5ms)  UPDATE "foos" SET "bar" = $1 WHERE "foos"."bar = $1 [["bar", "baz2"]]
COMMIT

Если я изменю строку блокировки, чтобы рельсы думали, что нужно что-то сделать с коллекцией, например, добавить puts в начало или pluck в конец...

Foo.where(bar: "baz").lock.pluck(:id)

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

Таким образом, кажется, что rails правильно использует .lock для изменения SQL отношения, но нет элегантного способа фактически вызвать блокировку. Я делаю что-то не так или нам нужно использовать какой-то обходной путь (например, pluck), чтобы выполнить запрос?


person John Bachir    schedule 09.01.2020    source источник


Ответы (2)


ApplicationRecord.transaction do
  User.where(id: [1, 2]).lock!.map do |user|
    puts user.id
    user.id
  end
end

производит

   (0.2ms)  BEGIN
  User Load (0.6ms)  SELECT "users".* FROM "users" WHERE "users"."id" IN (1, 2) FOR UPDATE
1
2
   (0.9ms)  COMMIT
=> [1, 2]
person Pavel Mikhailyuk    schedule 09.01.2020
comment
@JohnBachir Это просто пример того, что действие в блоке защищено SELECT FOR UPDATE. - person Pavel Mikhailyuk; 11.01.2020
comment
@JohnBachir Может быть, я не понял твой первоначальный вопрос. Что ты ищешь? - person Pavel Mikhailyuk; 11.01.2020
comment
Я задал этот вопрос администратору баз данных: dba.stackexchange.com/questions/257188 - person John Bachir; 12.01.2020
comment
@JohnBachir UPDATE автоматически блокирует записи, как указано в первом комментарии @ dba.stackexchange. И мой ответ был о том, как выбрать рельсы для обновления в нескольких строках. - person Pavel Mikhailyuk; 12.01.2020

Вдохновленный обсуждением здесь: https://dba.stackexchange.com/questions/257188/

Я думаю, что лучшим решением общего назначения является

Foo.where(bar: "baz").order(:id).lock.pluck('')

Который будет производить

SELECT  FROM "foos" WHERE "foos"."bar" = 'baz' ORDER BY "foos"."id" ASC FOR UPDATE;
person John Bachir    schedule 13.01.2020
comment
Обновление. Я не уверен, что изменилось с тех пор, как я впервые экспериментировал с этим, но он либо никогда не работал, либо по какой-то причине перестал работать в моем проекте. YMMV - комментарий, если это сработало для вас или не сработало - person John Bachir; 23.12.2020