我有一個問題,即將用戶提供的excel文件中的大量記錄導入到數據庫中。這個邏輯工作正常,我使用ActiveRecord-import來減少數據庫調用次數。但是,如果文件太大,處理時間可能會過長,Heroku會返回超時。解決方案:Resque並將處理移至後臺作業。Rails + 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
這是工作的一個簡化版本,用較少的片列淨度:
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儀表板中,作業不會記錄爲失敗。他們得到執行,我可以看到stats頁面上'已處理'作業的增量。但正如我所說的數據庫保持不變。這是怎麼回事?我怎樣才能更清楚地調試工作?有沒有辦法與Pry一起進入?
你可以在'Sale.import sales'每次調用之前記錄'sales.count',以確保你正在用數據進行'import'調用嗎? – hoffm
這是一個好主意。儘管我對Resque非常陌生。有什麼方法可以打印到Resque日誌?那是什麼語法?我會只使用'logger.info「收集#{sales.count}銷售來導入」'或類似的東西? –
我想你會看到輸出,如果它只是被髮送到標準輸出,所以'放置'收集#{sales.count}銷售導入「'應該做的伎倆。我假設你用'rake resque:work'開始Resque工作? – hoffm