任务很简单,analytics_eligbile?在给定数据的位置时,方法总是返回false,所以基本上没有调用重击代码.我在开发中的样本数据中有大约200个帖子.发布has_one analytics_facet.
无论此处的内部逻辑/业务如何,此任务唯一要做的就是调用analytics_eligible?方法每2分钟200次.在4小时内,我的物理内存使用量为110MB,虚拟内存为200MB.只是为了做这么简单的事情!我甚至无法想象,如果用真实的生产数据对10,000个帖子进行真正的分析,这会占用多少内存!当然,它可能无法运行2分钟,更像每30分钟,但我认为它不会飞.
这是在Ubuntu 10.x 64位上运行ruby 1.9.7,rails 2.3.5.我的笔记本电脑有4GB内存,双核cpu.
轨道真的很糟糕还是我做错了什么?
Delayed::Worker.logger.info('RAM USAGE Job Start: ' + `pmap #{Process.pid} | tail -1`[10,40].strip) Post.not_expired.each do |p| if p.analytics_eligible? #this method is never called Post.find_for_analytics_update(p.id).update_analytics end end Delayed::Worker.logger.info('RAM USAGE Job End: ' + `pmap #{Process.pid} | tail -1`[10,40].strip) Delayed::Job.enqueue PeriodicAnalyticsJob.new(),2.minutes.from_now
发布模型
def analytics_eligible? vf = self.analytics_facet if self.total_ratings > 0 && vf.nil? return true elsif !vf.nil? && vf.last_update_tv > 0 ratio = self.total_ratings / vf.last_update_tv if (ratio - 1) >= Constants::FACET_UPDATE_ELIGIBILITY_DELTA return true end end return false end
解决方法
此外,当您调用“Post.not_expired.each”时,您将所有not_expired帖子加载到RAM中.更好的解决方案是find_in_batches,它一次只能将X记录加载到RAM中.
修复它可能是一件简单的事情:
def do_analytics Post.not_expired.find_in_batches(:batch_size => 100) do |batch| batch.each do |post| if post.analytics_eligible? #this method is never called Post.find_for_analytics_update(post.id).update_analytics end end end GC.start end do_analytics
这里发生了一些事情.首先,整个事物的作用域是一个函数,以防止变量冲突保持块迭代器的引用.接下来,find_in_batches一次从数据库中检索batch_size对象,并且只要您不构建对它们的引用,就可以在每次迭代运行后获得垃圾回收的资格,从而减少总内存使用量.最后,我们在方法结束时调用GC.start;这会强制GC开始扫描(你不想在实时应用程序中进行扫描,但由于这是一个后台工作,如果运行需要额外的300毫秒就没关系).如果返回nil,它也有非常明显的好处,这意味着该方法的结果是nil,这意味着我们不会意外地挂起从finder返回的AR实例.
使用这样的东西应该确保你不会得到泄漏的AR对象,并且应该大大提高性能和内存使用.你需要确保你没有在你的应用程序的其他地方泄漏(类变量,全局变量和类引用是最严重的违规者),但我怀疑这将解决你的问题.
所有这一切,在我看来,这是一个cron问题(定期重复工作),而不是DJ问题.您可以使用一次性分析解析器,每隔X分钟使用脚本/运行程序运行您的分析,由cron调用,它可以非常巧妙地清除任何潜在的内存泄漏或每次运行的误操作(因为整个过程在结束时终止)