BackgroundJob disparado várias vezes ao salvar (Rails 5, Sidekiq)

Nov 24 2020

Estou lidando com o upload de arquivos PDFs por Cloundinarymeio de trabalhos em segundo plano. Eu os coloco na fila de um after_saveretorno 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_saveretorno

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 UploadPdfJobtrabalho for enfileirado mais de uma vez. O PDF só será carregado uma vez. O primeiro trabalho será definido uploadedcomo 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

DaviGo Nov 25 2020 at 21:48

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