class Resque::Failure::MultipleWithRetrySuppression

A multiple failure backend, with retry suppression

For example: if you had a job that could retry 5 times, your failure backends are not notified unless the final retry attempt also fails.

Example:

require 'resque-retry'
require 'resque/failure/redis'

Resque::Failure::MultipleWithRetrySuppression.classes = [Resque::Failure::Redis]
Resque::Failure.backend = Resque::Failure::MultipleWithRetrySuppression

Public Class Methods

failure_key(retry_key) click to toggle source

Expose this for the hook’s use

@api public

# File lib/resque/failure/multiple_with_retry_suppression.rb, line 96
def self.failure_key(retry_key)
  'failure-' + retry_key
end

Public Instance Methods

save() click to toggle source

Called when the job fails

If the job will retry, suppress the failure from the other backends. Store the lastest failure information in redis, used by the web interface.

@api private

Calls superclass method
# File lib/resque/failure/multiple_with_retry_suppression.rb, line 29
def save
  args = args_from(payload)

  log_message 'failure backend save', args, exception

  retryable = retryable?
  job_being_retried = retryable && retrying?

  if !job_being_retried
    log_message(
      "#{retryable ? '' : 'non-'}retryable job is not being retried - sending failure to superclass",
      args,
      exception
    )

    cleanup_retry_failure_log!
    return super
  end

  # some plugins define retry_delay and have it take no arguments, so rather than break those,
  # we'll just check here to see whether it takes the additional exception class argument or not
  # we also allow all job args to be passed to a custom `retry_delay` method
  retry_delay_arity = klass.method(:retry_delay).arity

  calculated_retry_delay = if [-2, 2].include?(retry_delay_arity)
    klass.retry_delay(exception.class, *args)
  elsif [-1, 1].include?(retry_delay_arity)
    klass.retry_delay(exception.class)
  else
    klass.retry_delay
  end

  if calculated_retry_delay > 0
    log_message(
      "retry_delay: #{calculated_retry_delay} > 0 - saving details in Redis",
      args,
      exception
    )

    data = {
      :failed_at => Time.now.strftime("%Y/%m/%d %H:%M:%S"),
      :payload   => payload,
      :exception => exception.class.to_s,
      :error     => exception.to_s,
      :backtrace => Array(exception.backtrace),
      :worker    => worker.to_s,
      :queue     => queue
    }
    data = Resque.encode(data)

    Resque.redis.setex(
      failure_key,
      2 * calculated_retry_delay,
      data
    )
  else
    log_message(
      "retry_delay: #{calculated_retry_delay} <= 0 - ignoring",
      args,
      exception
    )
  end
end

Protected Instance Methods

args_from(payload) click to toggle source
# File lib/resque/failure/multiple_with_retry_suppression.rb, line 119
def args_from(payload)
  (payload || {})['args'].dup
end
cleanup_retry_failure_log!() click to toggle source
# File lib/resque/failure/multiple_with_retry_suppression.rb, line 123
def cleanup_retry_failure_log!
  Resque.redis.del(failure_key) if retryable?
end
failure_key() click to toggle source
# File lib/resque/failure/multiple_with_retry_suppression.rb, line 127
def failure_key
  self.class.failure_key(retry_key)
end
klass() click to toggle source
# File lib/resque/failure/multiple_with_retry_suppression.rb, line 131
def klass
  Resque::Job.new(nil, nil).constantize(payload['class'])
end
retry_key() click to toggle source
# File lib/resque/failure/multiple_with_retry_suppression.rb, line 135
def retry_key
  klass.redis_retry_key(*payload['args'])
end
retryable?() click to toggle source
# File lib/resque/failure/multiple_with_retry_suppression.rb, line 139
def retryable?
  klass.respond_to?(:redis_retry_key)
rescue NameError
  false
end
retrying?() click to toggle source
# File lib/resque/failure/multiple_with_retry_suppression.rb, line 145
def retrying?
  redis_key_exists?(retry_key)
end

Private Instance Methods

redis_key_exists?(key) click to toggle source
# File lib/resque/failure/multiple_with_retry_suppression.rb, line 151
def redis_key_exists?(key)
  ![false, 0].include?(Resque.redis.exists(key) || false)
end