У меня есть рабочий веб-сайт со следующей средой:
- 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: Мы решили не использовать потоки для обработки заданий - это решит все наши проблемы: каждый обработчик заданий будет отдельным процессом:)