BackgroundJob se activa varias veces al guardar (Rails 5, Sidekiq)
Estoy manejando la carga de archivos PDF a Cloundinarytravés de trabajos en segundo plano. Los pongo en cola desde una after_savedevolución de llamada. El dilema es que, por una actualización, mi trabajo en segundo plano se activa varias veces. Para contrarrestar esta falla, traté de implementar un método usando around_perform, para asegurarme de que mi trabajo se activará solo una vez. Pero en realidad no funcionó. Me preguntaba si alguno de ustedes sabe cómo manejar esas llamadas no deseadas al trabajo.
Aqui esta mi codigo
Mi after_savedevolución de llamada
La devolución de llamada se coloca en mi modelo de factura y presupuesto.
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
fin
Mi trabajo 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
Mi around_perform
En esta parte, pongo mi instancia en una variable llamada element.
La idea era que, si el UploadPdfJobtrabajo se pone en cola más de una vez. El PDF solo se cargará una vez. El primer trabajo se establecerá uploadeden true, luego el segundo trabajo saldrá después de verificardone
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
Además, como no quería activar la devolución de llamada en la actualización, lo intenté de esta manera. Usando una variable llamada start, que no depende de mi instancia recuperada
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
Respuestas
Tuve que arremangarme, pero finalmente obtuve la última palabra. Debugé con binding.pry, llegué a la raíz del problema y descubrí y actualicé cada parte de los códigos que estaban activando mi trabajo.
Además, para prevenir aún más cualquier activación no deseada, agregué una devolución de llamada de guardia personalizada que indica qué parámetros guardados deben poner el trabajo en movimiento.
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