Arel::Table @aliases 在 Rails 5 中使用时发生内存泄漏

Arel::Table @aliases memory leak when used in Rails 5

一句话总结问题:什么会导致 Arel::Table@aliases 数组在每次使用 ActiveRecord 进行搜索时变大?

我使用Rails 5 编写了一个简单的网络应用程序。当我对其进行负载测试时,内存使用量无限增加。在 220 万次请求之后,该进程的常驻内存大小达到了 1GB 左右。

我通过在执行包含 100,000 个请求的负载测试之前、之后和之后 10 分钟获取堆转储进行调查。我使用 the heap dump diffing tool found here. It says that there were about 398,000 leaked objects created by Arel::Table#alias().

分析了堆转储

这句话似乎是罪魁祸首:

@aliases << node

我通过在我安装的 Arel::Table#alias() 版本中添加对 uniq! 的调用来确认 Arel::Table@aliases 数组是内存泄漏的来源:

def alias name = "#{self.name}_2"
  Nodes::TableAlias.new(self, name).tap do |node|
    @aliases << node
  end
  @aliases.uniq! # locally added this line
end

对 Arel 进行此修改后,我的应用程序的内存使用量在负载测试期间保持平稳。

据我所知,@aliases 随着对我的应用程序的每个请求而增长,并且它随着相同的对象而增长。我想知道这是否是 Arel 中的一个错误,或者我在我的应用程序中做了一些不好的事情导致这个数组在没有被清除或垃圾收集的情况下增长。

该应用有四个模型:

型号

class DeviceVendor < ApplicationRecord
end

class Group < ApplicationRecord
end

class RadiusDevice < ApplicationRecord
  belongs_to :device_vendor
  validates :ipv4_address, :ip => {format: :v4}
end

class RadiusVsa < ApplicationRecord
  belongs_to :group
  belongs_to :device_vendor
end

迁移

class CreateGroups < ActiveRecord::Migration[5.0]
  def change
    create_table :groups do |t|
      t.string :dn
      t.integer :rank

      t.timestamps
    end
  end
end

class CreateDeviceVendors < ActiveRecord::Migration[5.0]
  def change
    create_table :device_vendors do |t|
      t.string :name

      t.timestamps
    end
  end
end

class CreateRadiusDevices < ActiveRecord::Migration[5.0]
  def change
    create_table :radius_devices do |t|
      t.string :ipv4_address
      t.string :model_number
      t.belongs_to :device_vendor, index: true, foreign_key: true

      t.timestamps
    end
  end
end

class CreateRadiusVsas < ActiveRecord::Migration[5.0]
  def change
    create_table :radius_vsas do |t|
      t.string :radius_attributes
      t.belongs_to :device_vendor, index: true, foreign_key: true
      t.belongs_to :group, index: true, foreign_key: true

      t.timestamps
    end

  end
end

我唯一的 HTTP 端点根据 Group.dnRadiusDevice.ipv4_address 的输入参数搜索 RadiusVsa。以下是涉及的 ActiveRecord 调用:

# groups param value is like: ['ou=foo,cn=bar', 'ou=baz,cn=qux']
group = Group.order(rank: :desc).find_by!(dn: params.require('groups'))
# source param value is like: '10.0.0.1'
radius_device = RadiusDevice.find_by!(ipv4_address: params.require('source'))
# RadiusVsa.find_by! is the call that causes Arel::Table#alias() to be invoked
vendor_attributes = RadiusVsa.find_by!(group: group, device_vendor: radius_device.device_vendor)

原来这个内存泄漏是Arel中的一个bug。在一天没有收到回复后,我们创建了 an issue in Rails,现在已经修复了。