BackgroundJob se activa varias veces al guardar (Rails 5, Sidekiq)

Nov 24 2020

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

DaviGo Nov 25 2020 at 21:48

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