API响应数据集上的Ruby分页导致内存峰值
当我通过API返回的数据集进行分页时,我遇到了一个大内存峰值问题。API返回了约150k条记录,我一次请求10k条记录,并对15页数据进行分页。数据是一个哈希数组,每个哈希包含25个键和大约50个字符串值。这个过程杀死了我的512mb Heroku dyno 我有一个用于分页API响应数据集的方法API响应数据集上的Ruby分页导致内存峰值,ruby,out-of-memory,Ruby,Out Of Memory,当我通过API返回的数据集进行分页时,我遇到了一个大内存峰值问题。API返回了约150k条记录,我一次请求10k条记录,并对15页数据进行分页。数据是一个哈希数组,每个哈希包含25个键和大约50个字符串值。这个过程杀死了我的512mb Heroku dyno 我有一个用于分页API响应数据集的方法 def all_pages value_key = 'values', &block response = {} values = [] current_page = 1 to
def all_pages value_key = 'values', &block
response = {}
values = []
current_page = 1
total_pages = 1
offset = 0
begin
response = yield offset
#The following seems to be the culprit
values += response[value_key] if response.key? value_key
offset = response['offset']
total_pages = (response['totalResults'].to_f / response['limit'].to_f).ceil if response.key? 'totalResults'
end while (current_page += 1) <= total_pages
values
end
我知道是阵列的串联导致了问题,因为删除该行允许进程在没有内存问题的情况下运行。我做错了什么?整个数据集可能不超过20mb—这是如何消耗所有dyno内存的?我能做些什么来提高这里的效率
更新
响应如下所示:{“totalResults”:208904,“offset”:0,“count”:1,“hasMore”:true,limit:“10000”,“items”:[…])
更新2
使用报告运行
显示以下内容:
[HTTParty] [2014-08-13 13:11:22 -0700] 200 "GET 29259/data" -
Memory 171072KB
[HTTParty] [2014-08-13 13:11:26 -0700] 200 "GET 29259/data" -
Memory 211960KB
... removed for brevity ...
[HTTParty] [2014-08-13 13:12:28 -0700] 200 "GET 29259/data" -
Memory 875760KB
[HTTParty] [2014-08-13 13:12:33 -0700] 200 "GET 29259/data" -
Errno::ENOMEM: Cannot allocate memory - ps ax -o pid,rss | grep -E "^[[:space:]]*23137"
更新3
我可以用下面的基本脚本重新创建问题。该脚本是硬编码的,只能提取100k条记录,并且在我的本地VM上已经消耗了超过512MB的内存
#! /usr/bin/ruby
require 'uri'
require 'net/http'
require 'json'
uri = URI.parse("https://someapi.com/data")
offset = 0
values = []
begin
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.set_debug_output($stdout)
request = Net::HTTP::Get.new(uri.request_uri + "?limit=10000&offset=#{offset}")
request.add_field("Content-Type", "application/json")
request.add_field("Accept", "application/json")
response = http.request(request)
json_response = JSON.parse(response.body)
values << json_response['items']
offset += 10000
end while offset < 100_000
values
#/usr/bin/ruby
需要“uri”
需要“net/http”
需要“json”
uri=uri.parse(“https://someapi.com/data")
偏移量=0
值=[]
开始
http=Net::http.new(uri.host,uri.port)
http.use_ssl=true
http.set\u debug\u输出($stdout)
request=Net::HTTP::Get.new(uri.request_uri+“?limit=10000&offset={offset}”)
添加字段(“内容类型”、“应用程序/json”)
添加字段(“接受”、“应用程序/json”)
response=http.request(请求)
json_response=json.parse(response.body)
值您已经确定了将+=
用于阵列的问题。因此,可能的解决方案是添加数据,而每次都不创建新数组
values.push响应[value\u key]如果是response.key?值\u键
或者在没有任何运气的情况下使用我尝试过的推送
和concat。我将尝试否-使用检查当前页面
与可能未定义的总页面
仍然可以看到相同的内容。您介意在这里发布回复吗?它是否包含limit
键?Response没有limit键,如果不将新的响应值添加到values
中,我认为这与问题消失无关。如果Response没有limit
键,那么Response['limit']
是关于什么的?这个问题看起来像是被诱导出来的(例如,你的迭代次数比你预期的要多。)啊哈,在看了你发布的回复之后:你介意计算总页数吗?这样它就不会转到INF
?我建议您也将limit传递给block,并在(当前页面+=limit)
时循环。不幸的是,我不能在附加基准之外说什么。您是否介意定义def报告;将'Memory'+`ps ax-o pid,rss|grep-E“^[:space:][]*.{$$}”`.strip.split.map(&:to_i)[1]放到_s+'KB';结束
并将
报告
放在对数组+=…
的调用之后?可能需要尝试使用和不使用ObjectSpace.garbage\u collect
。我去年遇到了[可能是相同的]问题:另一方面,看起来您的数据不应该增加内部ruby堆。请在您自己的机器上打印内存报告(我很好奇ruby是否会释放内存),并尝试将ObjectSpace.garbage\u collect
放在JSON.parse
之后。
#! /usr/bin/ruby
require 'uri'
require 'net/http'
require 'json'
uri = URI.parse("https://someapi.com/data")
offset = 0
values = []
begin
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.set_debug_output($stdout)
request = Net::HTTP::Get.new(uri.request_uri + "?limit=10000&offset=#{offset}")
request.add_field("Content-Type", "application/json")
request.add_field("Accept", "application/json")
response = http.request(request)
json_response = JSON.parse(response.body)
values << json_response['items']
offset += 10000
end while offset < 100_000
values