BackgroundJob wurde beim Speichern mehrmals ausgelöst (Rails 5, Sidekiq)

Nov 24 2020

Ich kümmere mich um das Hochladen von PDF-Dateien Cloundinaryüber Hintergrundjobs. Ich stelle sie aus einem after_saveRückruf in die Warteschlange . Das Dilemna ist, dass bei einem Update mein Hintergrundjob mehrmals ausgelöst wird. Um diesem Fehler entgegenzuwirken, habe ich versucht, eine Methode zu implementieren around_perform, um sicherzustellen, dass mein Job nur einmal ausgelöst wird. Aber es hat tatsächlich nicht funktioniert. Ich habe mich gefragt, ob einer von Ihnen weiß, wie er mit diesen unerwünschten Anrufen umgehen soll

Hier ist mein Code

Mein after_saveRückruf

Der Rückruf wird sowohl auf meiner Modellrechnung als auch auf meinem Angebot platziert.

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

Ende

Mein 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

Meine around_perform

In diesem Teil habe ich meine Instanz in eine Variable mit dem Namen eingefügt element.

Die Idee war, dass, wenn der UploadPdfJobJob mehr als einmal in die Warteschlange gestellt wird. Das PDF wird nur einmal hochgeladen. Die erste Aufgabe wird festgelegt uploadedauf true, dann wird der zweite Job nach Prüfung verlassen wirddone

  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

Da ich den Rückruf beim Update nicht auslösen wollte, habe ich dies auch versucht. Wenn Sie eine Variable mit dem Namen verwenden start, hängt dies nicht von meiner abgerufenen Instanz ab

    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

Antworten

DaviGo Nov 25 2020 at 21:48

Ich musste meine Ärmel hochkrempeln, aber ich bekam endlich das letzte Wort. Ich debuggte mit binding.pry, ging dem Problem auf den Grund und fand und aktualisierte jeden Teil der Codes, die meinen Job auslösten.

Um unerwünschte Auslösungen weiter zu verhindern, habe ich einen benutzerdefinierten Guard-Rückruf hinzugefügt, der angibt, welche gespeicherten Parameter den Job in Gang setzen sollen.

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