我有一个具有以下环境的生产网站:
- 导轨 2.3.5
- MySQL 服务器 5.1.33
- Enterprise Ruby 1.8.6(2008-08-11 补丁级别 287)[x86_64-linux]
- mysql 宝石 2.7
- 旧版本的 BackgrounDRb 插件在 4 个不同的服务器上运行用于后台任务,每个有 5 个不同的工作人员(Ruby 线程,而不是单独的进程!)。
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
上面的代码尝试使用给定的 ID 和 in_process 字段的额外条件来更新记录。因此,如果同一记录已由不同的服务器/进程更新,则 UPDATE 语句将仅返回 0(零),并且该作业不会由 2 个不同的服务器同时处理。
问题是:有时“Job.connection.update(update_sql)”即使实际更新了记录也会返回 0(零)!只有在代码中添加了大量日志记录后,我才能发现这一点。它只发生在晚上我们负载很重的生产中......
我的猜测是 mysql gem 使用了一些全局变量(类变量)来影响 BackgrounDRb 进程的所有 5 个线程,但我不确定。我正在查看 mysql gem 和 ActiveRecord 的代码,但我不明白它是如何工作的。
这怎么可能发生?
2010-07-07 更新:我们决定不使用线程进行作业处理——这将解决我们所有的问题:每个作业处理器都是一个单独的进程 :)