我们的处理服务器主要与TempFiles一起工作,因为它让我们这边的事情变得更容易:不需要在垃圾收集或处理名称冲突时删除它们。
最近,我们遇到了TempFiles在这个过程中过早获得GCed的问题。特别是我们的一项服务,它将把Foo
文件从url转换为某个Bar
文件并上传到我们的服务器。
为了清晰起见,我在下面添加了一个案例场景,以便更容易进行讨论,并手头有一个示例。
此工作流程执行以下操作:
- 获取url作为参数
- 将Foo文件下载为TempFile
- 将其复制到新的TempFile
- 将相关资产下载到TempFiles
- 将相关资产链接到本地dup TempFile
- 将Foo转换为Bar格式
- 上传到我们的服务器
有时转换失败,一切都表明我们的本地Foo文件指向的是在转换前创建和GCed的相关资产。
我的两个问题:
-
我的TempFiles是否可能过早获得GCed?我读过关于Ruby GCed系统的文章——为了避免这些场景,它非常保守。
-
我该如何避免这种情况的发生?我可以尝试保存
download_and_replace_uri(node)
中的所有相关资产,并将它们作为返回传递,以在ConvertService
的实例仍然存在的情况下使其保持活动状态。但我不确定这是否能解决问题
myfile.foo
{
"buffers": [
{ "uri": "http://example.com/any_file.jpg" },
{ "uri": "http://example.com/any_file.png" },
{ "uri": "http://example.com/any_file.jpmp3" }
]
}
main.rb
ConvertService.new('http://example.com/myfile.foo')
转换器服务
class ConvertService
def initialize(url)
@url = url
@bar_file = Tempfile.new
end
def call
import_foo
convert_foo
upload_bar
end
private
def import_foo
@foo_file = ImportService.new(@url).call.edited_file
end
def convert_foo
`create-bar "#{@foo_file.path}" "#{@bar_file.path}"`
end
def upload_bar
UploadBarService.new(@bar_file).call
end
end
导入服务
class ImportService
def initialize(url)
@url = url
@edited_file ||= Tempfile.new
end
def call
download
duplicate
replace
end
private
def download
@original = DownloadFileService.new(@url).call.file
end
def duplicate
FileUtils.cp(@original.path, @edited_file.path)
end
def replace
file = File.read(@edited_file.path)
json = JSON.parse(file, symbolize_names: true)
json[:buffers]&.each do |node|
node[:uri] = DownloadFileService.new(node[:uri]).call.file.path
end
write_to_disk(@edited_file.path, json.to_json)
end
end
下载文件服务
module Helper
class DownloadFileService < ApplicationHelperService
def initialize(url)
@url = url
@file = Tempfile.new
end
def call
uri = URI.parse(@url)
Net::HTTP.start(
uri.host,
uri.port,
use_ssl: uri.scheme == 'https'
) do |http|
response = http.request(Net::HTTP::Get.new(uri.path))
@file.binmode
@file.write(response.body)
@file.flush
end
end
end
end
UploadBarService
module Helper
class UploadBarService < ApplicationHelperService
def initialize(file)
@file = file
end
def call
HTTParty.post('http://example.com/upload', body: { file: @file })
# NOTE: End points returns the url for the uploaded file
end
end
end
由于代码的复杂性和可能会混淆的缺失部分,解决问题的简单方法是确保您的tempfile实例对象在需要它们的整个生命周期中都保留在内存中,否则它们将立即被垃圾收集,从文件系统中删除tempfile,并将导致您遇到的tempfile状态丢失。
Tempfile的Ruby文档声明";当Tempfile对象被垃圾回收时,或者当Ruby解释器退出时,其关联的临时文件会自动删除">
根据评论,当遇到这个问题时,其他人可能会觉得这个对话很有帮助。