BackgroundJobは保存時に複数回トリガーされました(Rails 5、Sidekiq)

Nov 24 2020

PDFのファイルのアップロードをCloundinaryバックグラウンドジョブで処理しています。after_saveコールバックからそれらをキューに入れます。ジレンマは、1回の更新で、バックグラウンドジョブが複数回トリガーされることです。この欠陥に対処するために、を使用してメソッドを実装around_performし、ジョブが1回だけトリガーされるようにしました。しかし、実際には機能しませんでした。仕事への不要な電話を処理する方法を知っている人はいないかと思いました

これが私のコードです

私のafter_saveコールバック

コールバックは、モデルの請求書と見積もりの​​両方に配置されます。

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

終わり

私の仕事 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

僕の around_perform

この部分では、インスタンスをelement。という名前の変数に入れます。

アイデアは、UploadPdfJobジョブが複数回キューに入れられた場合です。PDFは一度だけアップロードされます。最初のジョブはに設定さuploadedtrue、2番目のジョブはチェック後に終了しますdone

  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

また、更新時にコールバックをトリガーしたくなかったので、この方法を試しました。start取得したインスタンスに依存しない、という変数を使用する

    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

回答

DaviGo Nov 25 2020 at 21:48

私は袖をまくり上げなければなりませんでした、しかし私はついに最後の言葉を得ました。私はでデバッグしbinding.pry、問題の根本に到達し、ジョブをトリガーしていたコードのすべての部分を見つけて更新しました。

また、不要なトリガーをさらに防ぐために、保存されたパラメーターがジョブを開始する必要があることを示すカスタムガードコールバックを追加しました。

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