BackgroundJob disparado várias vezes ao salvar (Rails 5, Sidekiq)
Estou lidando com o upload de arquivos PDFs por Cloundinary
meio de trabalhos em segundo plano. Eu os coloco na fila de um after_save
retorno de chamada. O dilema é que, para uma atualização, meu trabalho em segundo plano é acionado várias vezes. Para evitar essa falha, tentei implementar um método usando around_perform
, para garantir que meu trabalho fosse acionado apenas uma vez. Mas realmente não funcionou. Eu queria saber se algum de vocês sabe como lidar com essas chamadas indesejadas para o trabalho
Aqui está meu código
Meu after_save
retorno
O retorno de chamada é colocado em meu modelo de fatura e orçamento.
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
fim
Meu trabalho 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
Minhas around_perform
Nesta parte, coloquei minha instância em uma variável chamada element
.
A ideia era essa, se o UploadPdfJob
trabalho for enfileirado mais de uma vez. O PDF só será carregado uma vez. O primeiro trabalho será definido uploaded
como e true
, em seguida, o segundo trabalho será encerrado após a verificaçãodone
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
Além disso, como não queria acionar o callback na atualização, tentei desta forma. Usando uma variável chamada start
, que não depende da minha instância 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
Respostas
Tive de arregaçar as mangas, mas finalmente consegui a última palavra. Eu depurei com o binding.pry
, cheguei à raiz do problema e descobri e atualizei todas as partes dos códigos que estavam acionando meu Job.
Além disso, para evitar qualquer acionamento indesejado, adicionei um retorno de chamada de guarda personalizado, informando quais parâmetros salvos devem colocar o trabalho em movimento.
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