Rails ActiveJob无法感知数据库更新问题求助
问题分析与解决方案
从你的控制台日志里的CACHE标记就能一眼看出问题:Rails的查询缓存在ActiveJob的整个执行周期内都生效了,导致循环里的每次查询都直接复用了第一次的缓存结果,根本没去数据库拉取最新的变更。
为什么直接调用ReImport.watch_batch正常?因为在普通请求或控制台里,Rails会在请求结束/命令执行完后自动清理查询缓存;但ActiveJob是一个持续运行的后台任务,查询缓存会一直保留到Job执行结束,所以后续的查询全命中了缓存,看不到数据库的变化。
快速修复方案:禁用查询缓存
你可以用ActiveRecord::Base.uncached块包裹循环内的数据库查询,强制每次都去数据库获取最新数据:
修改ReImport模型的watch_batch方法:
def self.watch_batch(num) batch = ReImport.where(batch: num) total = batch.count remaining = batch.where(completed: false) remaining_count = remaining.count p "initial remaining: #{remaining_count}" while !remaining.empty? do p 'wait 10 seconds...' sleep(10) p '...seconds finished...' # 用uncached块包裹所有需要获取最新数据的查询 ActiveRecord::Base.uncached do batch = ReImport.where(batch: num) remaining = batch.where(completed: false) current_remaining_count = remaining.count p "remaining_count: #{remaining_count}" p "remaining . count: #{current_remaining_count}" p "(remaining_count > current_remaining_count): #{(remaining_count > current_remaining_count)}" if remaining_count > current_remaining_count p '=== WATCH_BATCH: update found! ===' remaining_count = current_remaining_count ReImportBatchChannel.broadcast_to("re_import_batch_#{num}", {update: true}) else p '=== WATCH_BATCH: nothing changed yet ===' end end end p '=== WATCH_BATCH COMPLETED (or timeout reached) ===' end
也可以在每次sleep后手动清除查询缓存:
sleep(10) ActiveRecord::Base.connection.clear_query_cache # 清除缓存 # 然后执行查询
不过uncached块更优雅,它只会在块内禁用缓存,不会影响Job里的其他逻辑。
更优的架构建议:替换轮询为事件触发
这种每隔10秒轮询数据库的方式其实很浪费资源,而且延迟高。更好的做法是在数据变更时主动触发广播,完全不需要后台Job轮询:
在ReImport模型里添加回调,当completed字段变更时自动发送广播:
class ReImport < ApplicationRecord # 当completed字段被修改并保存后触发 after_update :broadcast_batch_update, if: :saved_change_to_completed? private def broadcast_batch_update # 向对应的频道广播更新 ReImportBatchChannel.broadcast_to("re_import_batch_#{batch}", {update: true}) end end
这样只要Project A更新了completed字段,客户端就能立刻收到更新通知,既高效又实时,还省去了维护后台Job的麻烦。
内容的提问来源于stack exchange,提问作者skplunkerin
相关产品推荐
相关产品推荐

