ActiveRecord :: Base.connection.update (sql) иногда возвращает неверное количество затронутых строк - PullRequest
1 голос
/ 29 июня 2010

У меня есть рабочий веб-сайт со следующей средой:

  • Rails 2.3.5
  • MySQL Server 5.1.33
  • Enterprise Ruby 1.8.6 (2008-08-11 patchlevel 287) [x86_64-linux]
  • mysql gem 2.7
  • Старая версия плагина BackgrounDRb, работающая на 4 разных серверах для фоновых задач, с 5 разными работниками (* 1012)* Рубиновые потоки, а не отдельные процессы! ).

Один из рабочих BackgrounDRb обрабатывает очередь заданий с использованием варианта «оптимистической блокировки»:

    update_sql = "update jobs
                  set updated_at = CURRENT_TIMESTAMP,
                      in_process = 1
                  where id = #{job.id} and in_process = 0"

    affected_rows = Job.connection.update(update_sql)
    captured_job = affected_rows > 0 ? Job.find(job.id) : nil

Приведенный выше код пытается обновить запись с заданным идентификатором и дополнительным условием для поля in_process.Таким образом, если эта же запись уже была обновлена ​​другим сервером / процессом, оператор UPDATE просто вернул бы 0 (ноль), и задание не было бы обработано одновременно двумя разными серверами.

Проблема заключается в следующем: иногда «Job.connection.update (update_sql)» возвращает 0 (ноль), даже когда запись была фактически обновлена!Я смог это выяснить только после того, как в код было добавлено тяжелое ведение журнала.Это происходит только ночью в Production, когда у нас большая нагрузка ...

Я предполагаю, что в mysql gem используется некоторая глобальная переменная (class-variable) для disabled_rows, которая используется всеми 5 потоками процесса BackgrounDRb,но я не уверен.Я просматривал код mysql gem и ActiveRecord, но я не мог понять, как это действительно работает.

Как это могло произойти?

Обновление 2010-07-07: Мы решили не использовать потоки для обработки заданий - это решит все наши проблемы: каждый обработчик заданий будет отдельным процессом:)

...