首页 > 解决方案 > 在 rails 中使用 limit 和 offset 以及 updated_at 和 find_each - 这会导致问题吗?

问题描述

我有一个 Ruby on Rails 项目,其中有数百万个具有不同 url 的产品。我有一个函数“test_response”,它检查 url 并为 Product 属性返回 true 或 false marked_as_broken,无论是以哪种方式保存 Product 并将其“updated_at”属性更新为当前时间戳。

由于这是一个非常繁琐的过程,我创建了一个任务,该任务依次启动 15 个任务,每个任务都有 N/15 个要检查的产品。例如,第一个应该检查从第一个到第 10.000 个,第二个从第 10.000 个到第 20.000 个等等,使用 limit 和 offset。

该脚本运行良好,它开始了 15 个进程,但过早地快速完成一个又一个脚本。它不会终止,它以“进程以状态 0 退出”结束。

我的猜测是,使用 find_each 和搜索 updated_at 以及实际上在运行脚本时更新“updated_at”会改变一切,并且不会使脚本按预期遍历 10.000 个项目,但我无法验证这一点。

做我在这里做的事情是否有本质上的错误。例如,“find_each”是否偶尔运行一个新的 sql 查询,每次提供完全不同的结果,而不是预期?我确实希望它提供相同的 10.000 -> 20.000,但只是将其分成几部分。

task :big_response_launcher => :environment do
  nbr_of_fps = Product.where(:marked_as_broken => false).where("updated_at < '" + 1.year.ago.to_date.to_s + "'").size.to_i
  nbr_of_processes = 15
  batch_size = ((nbr_of_fps / nbr_of_processes))-2
  heroku = PlatformAPI.connect_oauth(auth_code_provided_elsewhere)  
  (0..nbr_of_processes-1).each do |i|
    puts "Launching #{i.to_s}"
    current_offset = batch_size * i
    puts "rake big_response_tester[#{current_offset},#{batch_size}]"
    heroku.dyno.create('kopa', {
      :command => "rake big_response_tester[#{current_offset},#{batch_size}]",
      :attach => false
    }) 
  end

end

task :big_response_tester, [:current_offset, :batch_size] => :environment do |task,args|
  current_limit = args[:batch_size].to_i
  current_offset = args[:current_offset].to_i  
  puts "Launching with offset #{current_offset.to_s} and limit #{current_limit.to_s}"
  Product.where(:marked_as_broken => false).where("updated_at < '" + 1.year.ago.to_date.to_s + "'").limit(current_limit).offset(current_offset).find_each do |fp|
    fp.test_response
  end  
end

标签: ruby-on-railsfindlimitoffset

解决方案


正如许多人在评论中指出的那样,使用 find_each 似乎会忽略顺序和限制。我找到了这个似乎对我有用的答案(ActiveRecord find_each 结合了 limit 和 order )。它不是 100% 工作,但它是一个明确的改进。其余的似乎是内存问题,即我不能在 Heroku 上同时运行太多进程。


推荐阅读