я испытываю состояние гонки в ActiveRecord с PostgreSQL, где я читаю значение, то увеличивая его и вставить новую запись:PostgreSQL и ActiveRecord подвыборка для гонки условия
num = Foo.where(bar_id: 42).maximum(:number)
Foo.create!({
bar_id: 42,
number: num + 1
})
в масштабе, несколько потоков будет одновременно прочитайте, затем введите то же значение number
. Обертка этого в транзакции не фиксирует условие гонки, потому что SELECT не блокирует таблицу. Я не могу использовать автоматическое приращение, потому что number
не уникален, он уникален только при заданном bar_id
. Я вижу 3 возможных исправлений: (? Блокировку на уровне строк)
- Явное Используйте Postgres блокировки
- Используйте уникальное ограничение и повторить на сбой
Override сохранить использовать подзапрос (Тьфу!) , IE
INSERT INTO foo (bar_id, number) VALUES (42, (SELECT MAX(number) + 1 FROM foo WHERE bar_id = 42));
Все эти решения, кажется, что я бы реализовав большую часть из ActiveRecord::Base#save!
Есть более простой способ?
UPDATE: Я думал, что я нашел ответ с Foo.lock(true).where(bar_id: 42).maximum(:number)
но использует SELECT FOR UDPATE
, который не допускается по совокупности запросов
UPDATE 2: Я просто информированных нашим DBA, что даже если бы мы могли делать INSERT INTO foo (bar_id, number) VALUES (42, (SELECT MAX(number) + 1 FROM foo WHERE bar_id = 42));
, что ничего не исправить, так как SELECT, работает в другом замке, чем INSERT
Если бы он был рассчитан «на лету», он со временем изменился бы, а также подвергся одному и тому же состоянию гонки –