BackgroundJob在保存时触发了多次(Rails 5,Sidekiq) [英] BackgroundJob triggered multiple times on save ( Rails 5, Sidekiq)
问题描述
我正在通过后台作业处理在Cloundinary
上的PDF文件的上载.我从after_save
回调中将它们加入队列.问题在于,一次更新会使我的后台作业被触发多次.为了解决此缺陷,我尝试使用around_perform
实现一种方法,以确保仅一次触发我的作业.但是它实际上没有用.我想知道你们中的任何人是否知道如何处理那些对工作的不必要的呼叫
I am handling the upload of PDFs' files on Cloundinary
through a background jobs. I enqueue them from an after_save
callback. The dilemna is that for one update my background job get triggered multiples times. To counter this flaw, I tried to implement a method using around_perform
, to ensure that my job would be triggered only one time. But it actually did not work. I was wondering if any of you know how to handle those unwanted calls to the job
这是我的代码
我的after_save
回调
My after_save
callback
回调同时放在我的模型发票和报价上.
The callback is placed on both my model Invoice and Quote.
Class Invoice
after_save :upload_pdf
def upload_pdf
UploadPdfJob.perform_later(self.id,'invoice')
new_notif_paid = Notification.create(user: self.user,
category: "PDF",
content: "Your PDF #{self.reference}
is available ",
element: "invoice",
element_id: self.id)
end
结束
我的工作UploadPDFJob
My Job UploadPDFJob
def perform(id, type)
create_pdf_copy(id, type)
end
def create_pdf_copy(id, type)
wicked = WickedPdf.new
value = type == 'invoice'? Invoice.find(id) : Quote.find(id)
template_path = type == 'invoice'? 'invoices/show': 'quotes/show.html.erb'
file_type = type == 'invoice'? 'facture': 'devis'
pdf_html = ApplicationController.render(
locals: {
current_user: value.user,
},
assigns: {
"#{type}": value,
format: 'pdf'
},
template: template_path,
layout: 'pdf'
)
pdf_file = wicked.pdf_from_string(pdf_html,
page_size: 'A4',
orientation: "portrait",
lowquality: true,
zoom: 0.9,
dpi: 75
)
tempfile = Tempfile.new("#{file_type}-#{value.id}.pdf")
File.open(tempfile.path, 'wb') do |file|
file << pdf_file
end
tempfile.close
unless pdf_file.blank?
value.photo.attach(io: File.open(tempfile.path), filename: "#{file_type}-#{value.id}.pdf")
end
end
我的around_perform
My around_perform
在这一部分中,我将实例放在名为element
的变量中.
In this part, I put my instance in a variable named element
.
这个想法是,如果UploadPdfJob
作业被多次排队. PDF仅上传一次.第一个作业将uploaded
设置为true
,然后在检查done
The idea was that, if the UploadPdfJob
job is enqueued more than once. The PDF will only be uploaded once. The first job will set uploaded
to true
, then the second job will exit after checking done
around_perform do |job, block|
id = job.arguments.first
element = job.arguments.last == 'invoice'? Invoice.find(id) : Quote.find(id)
element.with_lock do
return if element.uploaded
if block.call
element.update(uploaded: true)
else
retry_job
end
end
此外,由于我不想在更新时触发回调,因此我尝试了这种方式.使用名为start
的变量,该变量不依赖于我检索到的实例
Also, as I did not want to trigger the callback on the update, I tried this way. Using a variable called start
, that does not rely on my retrieved instance
around_perform do |job, block|
id = job.arguments.first
element = job.arguments.last == 'invoice'? Invoice.find(id) : Quote.find(id)
start = false
element.with_lock do
return if start == true
if block.call
start = true
else
retry_job
end
end
end
推荐答案
我不得不袖手旁观,但是我终于有了硬道理.我用binding.pry
进行了调试,找到了问题的根本原因,找出并更新了触发我的工作的所有代码.
I had to roll up my sleeves, but I finally got the last word. I debugged with binding.pry
,got to the very root of the problem and find out and updated every part of codes that were triggering my Job.
为了进一步防止任何不必要的触发,我添加了一个自定义的警卫回调,指出保存的哪些参数应使作业运行.
Also to further prevent any unwanted triggering, I added a customed guard callback stating which parameters saved should set the Job in motion.
Class Invoice
after_save :upload_pdf, if: :should_upload?
def should_upload?
attributes = self.saved_changes
%i[title client_id creation_date].any? {|val| attributes.key? val}
end
end
这篇关于BackgroundJob在保存时触发了多次(Rails 5,Sidekiq)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!