Rails+resque 后台作业导入不向数据库添加任何内容
Rails+resque background job import not adding anything to the database
我在将大量记录从用户提供的 excel 文件导入数据库时遇到问题。这个逻辑工作正常,我正在使用 ActiveRecord-import 来减少数据库调用的次数。但是,当文件太大时,处理时间可能会太长,Heroku 将 return 超时。解决方案:Resque 并将处理移动到后台作业。
到目前为止,还不错。我需要添加 CarrierWave 以将文件上传到 S3,因为我不能只将文件保存在内存中以供后台作业使用。上传部分也工作正常,我为他们创建了一个模型,并将 ID 传递给排队的作业,以便稍后检索文件,据我所知,我无法将整个 ActiveRecord 对象传递给作业。
我已经在本地安装了 Resque 和 Redis,在这方面似乎一切都设置正确。我可以看到我正在创建的作业正在排队,然后 运行 没有失败。这项工作似乎 运行 没问题,但没有记录添加到数据库中。如果我在控制台中逐行 运行 我的工作代码,记录将按我的预期添加到数据库中。但是当我创建排队作业时 运行,没有任何反应。
我不太明白问题出在哪里。
这是我的上传控制器的创建操作:
def create
@upload = Upload.new(upload_params)
if @upload.save
Resque.enqueue(ExcelImportJob, @upload.id)
flash[:info] = 'File uploaded.
Data will be processed and added to the database.'
redirect_to root_path
else
flash[:warning] = 'Upload failed. Please try again.'
render :new
end
end
为了清楚起见,这是作业的简化版本,减少了 sheet 列:
class ExcelImportJob < ApplicationJob
@queue = :default
def perform(upload_id)
file = Upload.find(upload_id).file.file.file
data = parse_excel(file)
if header_matches? data
# Create a database entry for each row, ignoring the first header row
# using activerecord-import
sales = []
data.drop(1).each_with_index do |row, index|
sales << Sale.new(row)
if index % 2500 == 0
Sale.import sales
sales = []
end
end
Sale.import sales
end
def parse_excel(upload)
# Open the uploaded excel document
doc = Creek::Book.new upload
# Map rows to the hash keys from the database
doc.sheets.first.rows.map do |row|
{ date: row.values[0],
title: row.values[1],
author: row.values[2],
isbn: row.values[3],
release_date: row.values[5],
units_sold: row.values[6],
units_refunded: row.values[7],
net_units_sold: row.values[8],
payment_amount: row.values[9],
payment_amount_currency: row.values[10] }
end
end
# Returns true if header matches the expected format
def header_matches?(data)
data.first == {:date => 'Date',
:title => 'Title',
:author => 'Author',
:isbn => 'ISBN',
:release_date => 'Release Date',
:units_sold => 'Units Sold',
:units_refunded => 'Units Refunded',
:net_units_sold => 'Net Units Sold',
:payment_amount => 'Payment Amount',
:payment_amount_currency => 'Payment Amount Currency'}
end
end
end
无论如何,我可能会有一些改进的逻辑,因为现在我将整个文件保存在内存中,但这不是我遇到的问题——即使是只有 500 行左右的小文件,作业不会向数据库添加任何内容。
就像我说的,当我不使用后台作业时我的代码运行良好,如果我在控制台中 运行 它仍然可以运行。但由于某种原因,这项工作什么也没做。
这是我第一次使用 Resque,所以我不知道我是否遗漏了一些明显的东西?我确实创建了一个工人,正如我所说,它似乎 运行 这份工作。这是 Resque 详细格式化程序的输出:
*** resque-1.27.4: Waiting for default
*** Checking default
*** Found job on default
*** resque-1.27.4: Processing default since 1508342426 [ExcelImportJob]
*** got: (Job{default} | ExcelImportJob | [15])
*** Running before_fork hooks with [(Job{default} | ExcelImportJob | [15])]
*** resque-1.27.4: Forked 63706 at 1508342426
*** Running after_fork hooks with [(Job{default} | ExcelImportJob | [15])]
*** done: (Job{default} | ExcelImportJob | [15])
在 Resque 仪表板中,作业未记录为失败。它们被执行,我可以在统计页面上看到“已处理”作业的增加。但正如我所说,数据库保持不变。这是怎么回事?如何更清楚地调试作业?有没有办法用 Pry 进入它?
看来我的问题出在 Resque.enqueue(ExcelImportJob, @upload.id)
上。
我将代码更改为 ExcelImportJob.perform_later(@upload.id)
,现在我的代码实际上是 运行!
我还向 lib/tasks
添加了一个 resque.rake
任务,如下所述:http://bica.co/2015/01/20/active-job-resque/.
那个 link 还说明了如何使用 rails runner
调用作业而不 运行 连接完整的 Rails 服务器并触发作业,这对调试很有用.
奇怪的是,我并没有完全按照@hoffm 的建议将任何内容打印到 STDOUT,但至少它让我找到了一条很好的查询途径。
我仍然不完全理解为什么调用 Resqueue.enqueue 仍然将我的作业添加到队列中并且确实似乎 运行 它们之间的区别,但是代码没有被执行,所以如果有人有更好的理解和解释,将不胜感激。
TL;DR:调用 perform_later
而不是 Resque.enqueue
解决了问题,但我不知道为什么。
我在将大量记录从用户提供的 excel 文件导入数据库时遇到问题。这个逻辑工作正常,我正在使用 ActiveRecord-import 来减少数据库调用的次数。但是,当文件太大时,处理时间可能会太长,Heroku 将 return 超时。解决方案:Resque 并将处理移动到后台作业。
到目前为止,还不错。我需要添加 CarrierWave 以将文件上传到 S3,因为我不能只将文件保存在内存中以供后台作业使用。上传部分也工作正常,我为他们创建了一个模型,并将 ID 传递给排队的作业,以便稍后检索文件,据我所知,我无法将整个 ActiveRecord 对象传递给作业。
我已经在本地安装了 Resque 和 Redis,在这方面似乎一切都设置正确。我可以看到我正在创建的作业正在排队,然后 运行 没有失败。这项工作似乎 运行 没问题,但没有记录添加到数据库中。如果我在控制台中逐行 运行 我的工作代码,记录将按我的预期添加到数据库中。但是当我创建排队作业时 运行,没有任何反应。
我不太明白问题出在哪里。
这是我的上传控制器的创建操作:
def create
@upload = Upload.new(upload_params)
if @upload.save
Resque.enqueue(ExcelImportJob, @upload.id)
flash[:info] = 'File uploaded.
Data will be processed and added to the database.'
redirect_to root_path
else
flash[:warning] = 'Upload failed. Please try again.'
render :new
end
end
为了清楚起见,这是作业的简化版本,减少了 sheet 列:
class ExcelImportJob < ApplicationJob
@queue = :default
def perform(upload_id)
file = Upload.find(upload_id).file.file.file
data = parse_excel(file)
if header_matches? data
# Create a database entry for each row, ignoring the first header row
# using activerecord-import
sales = []
data.drop(1).each_with_index do |row, index|
sales << Sale.new(row)
if index % 2500 == 0
Sale.import sales
sales = []
end
end
Sale.import sales
end
def parse_excel(upload)
# Open the uploaded excel document
doc = Creek::Book.new upload
# Map rows to the hash keys from the database
doc.sheets.first.rows.map do |row|
{ date: row.values[0],
title: row.values[1],
author: row.values[2],
isbn: row.values[3],
release_date: row.values[5],
units_sold: row.values[6],
units_refunded: row.values[7],
net_units_sold: row.values[8],
payment_amount: row.values[9],
payment_amount_currency: row.values[10] }
end
end
# Returns true if header matches the expected format
def header_matches?(data)
data.first == {:date => 'Date',
:title => 'Title',
:author => 'Author',
:isbn => 'ISBN',
:release_date => 'Release Date',
:units_sold => 'Units Sold',
:units_refunded => 'Units Refunded',
:net_units_sold => 'Net Units Sold',
:payment_amount => 'Payment Amount',
:payment_amount_currency => 'Payment Amount Currency'}
end
end
end
无论如何,我可能会有一些改进的逻辑,因为现在我将整个文件保存在内存中,但这不是我遇到的问题——即使是只有 500 行左右的小文件,作业不会向数据库添加任何内容。
就像我说的,当我不使用后台作业时我的代码运行良好,如果我在控制台中 运行 它仍然可以运行。但由于某种原因,这项工作什么也没做。
这是我第一次使用 Resque,所以我不知道我是否遗漏了一些明显的东西?我确实创建了一个工人,正如我所说,它似乎 运行 这份工作。这是 Resque 详细格式化程序的输出:
*** resque-1.27.4: Waiting for default
*** Checking default
*** Found job on default
*** resque-1.27.4: Processing default since 1508342426 [ExcelImportJob]
*** got: (Job{default} | ExcelImportJob | [15])
*** Running before_fork hooks with [(Job{default} | ExcelImportJob | [15])]
*** resque-1.27.4: Forked 63706 at 1508342426
*** Running after_fork hooks with [(Job{default} | ExcelImportJob | [15])]
*** done: (Job{default} | ExcelImportJob | [15])
在 Resque 仪表板中,作业未记录为失败。它们被执行,我可以在统计页面上看到“已处理”作业的增加。但正如我所说,数据库保持不变。这是怎么回事?如何更清楚地调试作业?有没有办法用 Pry 进入它?
看来我的问题出在 Resque.enqueue(ExcelImportJob, @upload.id)
上。
我将代码更改为 ExcelImportJob.perform_later(@upload.id)
,现在我的代码实际上是 运行!
我还向 lib/tasks
添加了一个 resque.rake
任务,如下所述:http://bica.co/2015/01/20/active-job-resque/.
那个 link 还说明了如何使用 rails runner
调用作业而不 运行 连接完整的 Rails 服务器并触发作业,这对调试很有用.
奇怪的是,我并没有完全按照@hoffm 的建议将任何内容打印到 STDOUT,但至少它让我找到了一条很好的查询途径。
我仍然不完全理解为什么调用 Resqueue.enqueue 仍然将我的作业添加到队列中并且确实似乎 运行 它们之间的区别,但是代码没有被执行,所以如果有人有更好的理解和解释,将不胜感激。
TL;DR:调用 perform_later
而不是 Resque.enqueue
解决了问题,但我不知道为什么。