Как установить счетчик попыток для Sidekiq с помощью ActiveJob?

Из API Rails я обнаружил, что ActiveJob может retry_job интервал:

my_job_instance.enqueue
my_job_instance.enqueue wait: 5.minutes
my_job_instance.enqueue queue: :important
my_job_instance.enqueue wait_until: Date.tomorrow.midnight

http://api.rubyonrails.org/classes/ActiveJob/Enqueuing.html

Но если я хочу установить счетчик повторов, например Sidekiq's:

include Sidekiq::Worker
sidekiq_options :retry => 5

https://github.com/mperham/sidekiq/wiki/Error-Handling

Как это сделать в этом примере кода?

class SiteScrapperJob < ActiveJob::Base
  rescue_from(ErrorLoadingSite) do
    retry_job queue: :low_priority
  end

  def perform(*args)
    # raise ErrorLoadingSite if cannot scrape
  end
end

Теперь я добавил это в мой класс работы:

Sidekiq.default_worker_options = { retry: 5 }

Но это кажется не очень хорошим.

Ответ 1

Вас также может заинтересовать это решение, которое использует serialize и deserialize api для хранения количества попыток.

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

Возьми это отсюда.

Ответ 2

Вы не можете. Если вы хотите использовать специфичные для Sidekiq вещи, вам нужно использовать специфичные для Sidekiq API. ActiveJob не выставляет механизм повторной попытки Sidekiq.

Ответ 3

Начиная с Rails 5.1, есть встроенный способ сделать это, используя метод retry_on. Это общий метод ActiveJob, поэтому он будет работать с любым бэкэндом, не только с Sidekiq.

Например, для вашей конкретной работы вы можете сделать:

class SiteScraperJob < ActiveJob::Base
  retry_on ErrorLoadingSite, queue: :low_priority, attempts: 5

  def perform(*args)
    # raise ErrorLoadingSite if cannot scrape
  end
end

Вы также можете установить постоянный интервал ожидания или экспоненциальную стратегию ожидания, как описано в документации.

Ответ 4

См. здесь значения по умолчанию для Sidekiq. Атрибут retry "принимает" логическое значение, а не число, как вы предполагали.

Из слияния active_job в Rails этот другой файл можно увидеть, что еще раз retry не принимает количество попыток.

Что говорит документация, то это за задание, которое вы можете определить, если задание повторится или нет.

Я также попытался найти, может ли файл config/sidekiq.yml получить этот номер, и кажется, что он не может.

Наконец,

Если вы не исправите ошибку в течение 25 попыток (около 21 дня), Sidekiq прекратит повторную попытку и переместит вашу работу в Очередь мертвых заданий. Вы можете исправить ошибку и повторить задание вручную в любое время в течение следующих 6 месяцев с помощью веб-интерфейса.

Ответ 5

Существует sidekiq-retry, который выполняет работу

class SiteScrapperJob < ActiveJob::Base
  include ActiveJob::Retry.new(limit: 5, strategy: :exponential)

  def perform(*args)
    # raise ErrorLoadingSite if cannot scrape
  end
end

Другой вариант - использовать промежуточное ПО sidekiq:

Сначала определите класс-метод job_options, который будет доступен в подклассах:

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

  def self.get_job_options
    @job_options || {}
  end
end

Добавьте промежуточное программное обеспечение, которое считывает job_options из класса заданий и записывает их в элемент задания для 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

И наконец

 class SiteScrapperJob < ApplicationJob
   job_options retry: 5

   def perform
     # your code
   end
 end

Ответ 6

если вы просто используете sidekiq, backerver сервера обновлений, патч обезьяны может помочь вам

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

тогда вы можете написать, как показано ниже:

class BaseJob < ActiveJob::Base

  sidekiq_options retry: 2, queue: :low
  sidekiq_retry_in { |count, _| 3 * count }

  def perform; end
end

счастливое кодирование