何でも屋エンジニアのブログ

ソフトウェア関連技術、コミュニティ、日々の雑貨

ActiveJob のリトライの挙動

job の内部でネットワークエラーを拾うケースでは、定間隔より exponential backoff でリトライしたいなぁと考え調べた。

ActiveJob::Exceptions#retry_on で定義されている。

    def retry_on(*exceptions, wait: 3.seconds, attempts: 5, queue: nil, priority: nil, jitter: JITTER_DEFAULT)
        rescue_from(*exceptions) do |error|
          executions = executions_for(exceptions)
          if attempts == :unlimited || executions < attempts
            retry_job wait: determine_delay(seconds_or_duration_or_algorithm: wait, executions: executions, jitter: jitter), queue: queue, priority: priority, error: error
          else
            if block_given?
              instrument :retry_stopped, error: error do
                yield self, error
              end
            else
              instrument :retry_stopped, error: error
              raise error
            end
          end
        end
      end

rails/exceptions.rb at v7.0.4 · rails/rails · GitHub

間隔の決定は同クラスの #determine_delay 内で行っている。

  def determine_delay(seconds_or_duration_or_algorithm:, executions:, jitter: JITTER_DEFAULT)
        jitter = jitter == JITTER_DEFAULT ? self.class.retry_jitter : (jitter || 0.0)

        case seconds_or_duration_or_algorithm
        when :exponentially_longer
          delay = executions**4
          delay_jitter = determine_jitter_for_delay(delay, jitter)
          delay + delay_jitter + 2
        when ActiveSupport::Duration, Integer
          delay = seconds_or_duration_or_algorithm.to_i
          delay_jitter = determine_jitter_for_delay(delay, jitter)
          delay + delay_jitter
        when Proc
          algorithm = seconds_or_duration_or_algorithm
          algorithm.call(executions)
        else
          raise "Couldn't determine a delay based on #{seconds_or_duration_or_algorithm.inspect}"
        end
      end

rails/exceptions.rb at v7.0.4 · rails/rails · GitHub

ということで waitActiveSupport::Duration1.second など)を渡すと jitter はあるものの定間隔でリトライする。 wait:exponentially_longer を渡せば exponential backoff となる。