首页 > 解决方案 > BackgroundJob 在保存时触发多次(Rails 5,Sidekiq)

问题描述

我正在Cloundinary通过后台作业处理 PDF 文件的上传。我将它们从after_save回调中排入队列。困境是,对于一次更新,我的后台作业会被多次触发。为了解决这个缺陷,我尝试使用 , 来实现一种方法around_perform,以确保我的工作只会被触发一次。但实际上并没有奏效。我想知道你们中是否有人知道如何处理那些不需要的工作电话

这是我的代码

我的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,然后第二个作业将在检查后退出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

标签: ruby-on-railsjobssidekiq

解决方案


我不得不卷起袖子,但我终于说了算。我进行了调试binding.pry,找到了问题的根源,并找出并更新了触发我工作的代码的每一部分。

此外,为了进一步防止任何不必要的触发,我添加了一个自定义的保护回调,说明保存的哪些参数应该让 Job 启动。

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

推荐阅读