工作结束后,无法在工作者中释放内存

时间:2019-03-03 10:50:14

标签: ruby-on-rails ruby memory-management sidekiq activerecord-import

场景:

我有一份工作正在生产(heroku)中运行流程(sidekiq)。该过程使用activerecord-import gem将数据(CSV)从S3导入数据库模型。该gem帮助批量插入数据。因此,dbRows变量在迭代CSV行时从所有存储的ActiveRecord对象中设置了大量内存(都很好)。导入数据(在db_model.import dbRows中)后,dbRows被清除(应该是!),并处理下一个对象。

例如:(简化脚本以更好地理解)

def import
      ....
      s3_objects.contents.each do |obj|
          @cli.get_object({..., key: obj.key}, target: file) 
          dbRows = []
          csv = CSV.new(file, headers: false)
          while line = csv.shift
              # >> here dbRows grows and grows and never is freed!
              dbRows << db_model.new(
                field1: field1,
                field2: field2,
                fieldN: fieldN
              )
          end
          db_model.import dbRows
          dbRows = nil   # try 1 to freed array
          GC.start   # try 2 to freed memory
      end
      ....
end

问题:

作业完成后,进程运行BUT时,作业内存会增加,而内存不会下降。它永远存在!

调试中,我发现dbRows看起来永远不会被垃圾回收 我了解了其中的RETAINED对象以及内存在rails中的工作方式。尽管我还没有找到一种方法来解决我的问题。

我希望作业完成后,所有在dbRows上设置的引用都是GC,并且释放了工作人员内存。

任何帮助表示赞赏。

更新:我读到有关weakref的信息,但我不知道这是否有用。有什么见识吗?

1 个答案:

答案 0 :(得分:0)

尝试从CSV批量导入行,例如一次将行导入到DB 1000行中,这样就不必保留前几行,GC可以收集它们。在任何情况下,这对数据库都是有好处的(对于从s3下载,如果您从S3移交CSV IO对象,也是如此。

s3_io_object = s3_client.get_object(*s3_obj_params).body
csv = CSV.new(s3_io_object, headers: true, header_converters: :symbol)
csv.each_slice(1_000) do |row_batch|
  db_model.import ALLOWED_FIELDS, row_batch.map(&:to_h), validate: false
end

请注意,我既不实例化AR模型也不是为了节省内存,而只是传递散列并将activerecord-import告知validate: false

此外,file引用来自哪里?它似乎是长寿的。

从您的示例中看不出来,但是您的环境中的库或扩展仍可能全局保留对对象的引用吗?

有时候这些事情很难追踪,因为任何被调用的代码(包括外部库代码)都可以执行以下操作:

动态定义常量,因为它们永远不会得到GC

Any::Module::Or:Class.const_set('NewConstantName', :foo)

或将数据添加到常量引用/拥有的任何内容

SomeConstant::Referenceable::Globally.array << foo # array will only get bigger and contents will never be GC'd

否则,您能做的最好的就是使用一些内存分析工具,无论是在Ruby内部(内存分析gem)还是在Ruby外部(作业和系统日志)来尝试查找源。