How to set retry count for Sidekiq with ActiveJob?

后端 未结 7 2035
走了就别回头了
走了就别回头了 2021-02-07 01:50

From the Rails API, I found ActiveJob can retry_job interval:

my_job_instance.enqueue
my_job_instance.enqueue wait: 5.minutes
my_job_instance.enqueue queue: :imp         


        
相关标签:
7条回答
  • 2021-02-07 02:32

    You also might be interested in this solution which uses serialize and deserialize api to store the number of attempts.

    class DeliverWebhookJob < ActiveJob::Base
      def serialize
        super.merge('attempt_number' => (@attempt_number || 0) + 1)
      end
    
      def deserialize(job_data)
        super
        @attempt_number = job_data['attempt_number']
      end
    
      rescue_from(ErrorLoadingSite) do |exception|
        retry_job(wait: 10) if @attempt_number < 5
      end
    
      def perform(*args)
        # raise ErrorLoadingSite if cannot scrape
      end
    end
    

    Take it from here.

    0 讨论(0)
  • 2021-02-07 02:32

    if you just use sidekiq, nerver change backend, monkey patch can help you

    module ActiveJob
      module QueueAdapters
        class SidekiqAdapter
          def enqueue(job)
            JobWrapper.sidekiq_options job.sidekiq_options_hash if job.sidekiq_options_hash
            JobWrapper.sidekiq_retry_in job.sidekiq_retry_in_block if job.sidekiq_retry_in_block
            Sidekiq::Client.push(
              'class' => JobWrapper,
              'wrapped' => job.class.to_s,
              'queue' => job.queue_name,
              'args'  => [ job.serialize ]
            )
          end
    
          def enqueue_at(job, timestamp)
            JobWrapper.sidekiq_options job.sidekiq_options_hash if job.sidekiq_options_hash
            JobWrapper.sidekiq_retry_in job.sidekiq_retry_in_block if job.sidekiq_retry_in_block
            Sidekiq::Client.push(
              'class' => JobWrapper,
              'wrapped' => job.class.to_s,
              'queue' => job.queue_name,
              'args'  => [ job.serialize ],
              'at'    => timestamp
            )
          end
        end
      end
    
      class Base
        class_attribute :sidekiq_options_hash
        class_attribute :sidekiq_retry_in_block
    
        def self.sidekiq_options(opts={})
          self.sidekiq_options_hash = opts
        end
    
        def self.sidekiq_retry_in(&block)
          self.sidekiq_retry_in_block = block
        end
      end
    end
    

    then, you can write like below:

    class BaseJob < ActiveJob::Base
    
      sidekiq_options retry: 2, queue: :low
      sidekiq_retry_in { |count, _| 3 * count }
    
      def perform; end
    end
    

    happy coding

    0 讨论(0)
  • 2021-02-07 02:35

    See here the defaults for Sidekiq. The attribute retry "accepts" a boolean value and not a number as you assumed.

    From the merge of active_job into Rails this other file one can see that once again retry doesn't accept the number of retries.

    What the documentation says then is that per job you can define if the job retries or not.

    I also tried to find if the config/sidekiq.yml file can receive this number, and seems like it can't.

    Finally,

    If you don't fix the bug within 25 retries (about 21 days), Sidekiq will stop retrying and move your job to the Dead Job Queue. You can fix the bug and retry the job manually anytime within the next 6 months using the Web UI.

    0 讨论(0)
  • 2021-02-07 02:39

    Since Rails 5.1, there is a built-in way to do this using the retry_on method. It's a general ActiveJob method, so it will work with any queuing backend, not just Sidekiq.

    For example, for your specific job you could do:

    class SiteScraperJob < ActiveJob::Base
      retry_on ErrorLoadingSite, queue: :low_priority, attempts: 5
    
      def perform(*args)
        # raise ErrorLoadingSite if cannot scrape
      end
    end
    

    You can also set a constant wait interval or an exponential wait strategy, as explained in the docs.

    0 讨论(0)
  • 2021-02-07 02:39

    There is a activejob-retry gem which does the job

    class SiteScrapperJob < ActiveJob::Base
      include ActiveJob::Retry.new(limit: 5, strategy: :exponential)
    
      def perform(*args)
        # raise ErrorLoadingSite if cannot scrape
      end
    end
    

    Another option is to use sidekiq middleware:

    First define job_options class-method which will be available in the subclasses:

    class ApplicationJob < ActiveJob::Base
      def self.job_options(options)
        @job_options = options
      end
    
      def self.get_job_options
        @job_options || {}
      end
    end
    

    Add middleware which reads job_options from the jobs's class and writes them to the job item for sidekiq:

    module Sidekiq
     class JobOptionsMiddleware
    
       def call(job_wrapper, item, queue, redis_pool)
         job = item['args'][0]['job_class'].constantize
    
         job.get_job_options
           .each{ |option, value| item[option] = value if item[option].nil? }
    
         yield
       end
    
     end
    
     # in sidekiq initializer
    
     Sidekiq.configure_client do |config|
       config.client_middleware do |chain|
         chain.add Sidekiq::JobOptionsMiddleware
       end
     end
    

    And finally

     class SiteScrapperJob < ApplicationJob
       job_options retry: 5
    
       def perform
         # your code
       end
     end
    
    0 讨论(0)
  • 2021-02-07 02:39

    FYI this problem has been fixed in the release of Sidekiq 6.0. You can have a look at the changelog here: https://github.com/mperham/sidekiq/blob/master/Changes.md

    But basically you can pass the same options by calling sidekiq_options with your options hash. Thanks Mike Perham.

    0 讨论(0)
提交回复
热议问题