lib/resque/plugins/retry.rb in resque-retry-1.0.0.a vs lib/resque/plugins/retry.rb in resque-retry-1.0.0

- old
+ new

@@ -33,61 +33,73 @@ # end # module Retry # Copy retry criteria checks on inheritance. + # + # @api private def inherited(subclass) super(subclass) subclass.instance_variable_set("@retry_criteria_checks", retry_criteria_checks.dup) end # @abstract You may override to implement a custom retry identifier, # you should consider doing this if your job arguments # are many/long or may not cleanly convert to strings. # # Builds a retry identifier using the job arguments. This identifier - # is used as part of the redis key. + # is used as part of the redis key # # @param [Array] args job arguments # @return [String] job identifier + # + # @api public def retry_identifier(*args) args_string = args.join('-') args_string.empty? ? nil : args_string end # Builds the redis key to be used for keeping state of the job # attempts. # # @return [String] redis key + # + # @api public def redis_retry_key(*args) ['resque-retry', name, retry_identifier(*args)].compact.join(":").gsub(/\s/, '') end - # Maximum number of retrys we can attempt to successfully perform the job. + # Maximum number of retrys we can attempt to successfully perform the job # # A retry limit of 0 will *never* retry. # A retry limit of -1 or below will retry forever. # # @return [Fixnum] + # + # @api public def retry_limit @retry_limit ||= 1 end - # Number of retry attempts used to try and perform the job. + # Number of retry attempts used to try and perform the job # # The real value is kept in Redis, it is accessed and incremented using # a before_perform hook. # # @return [Fixnum] number of attempts + # + # @api public def retry_attempt @retry_attempt ||= 0 end # @abstract - # Number of seconds to delay until the job is retried. - # + # Number of seconds to delay until the job is retried + # # @return [Number] number of seconds to delay + # + # @api public def retry_delay(exception_class = nil) if @retry_exceptions.is_a?(Hash) delay = @retry_exceptions[exception_class] || 0 # allow an array of delays. delay.is_a?(Array) ? delay[retry_attempt] || delay.last : delay @@ -98,80 +110,110 @@ # @abstract # Number of seconds to sleep after job is requeued # # @return [Number] number of seconds to sleep + # + # @api public def sleep_after_requeue @sleep_after_requeue ||= 0 end + # @abstract + # Specify another resque job (module or class) to delegate retry duties + # to upon failure + # + # @return [Object, nil] class or module if delegate on failure, otherwise nil + # + # @api public def retry_job_delegate @retry_job_delegate ||= nil end # @abstract # Modify the arguments used to retry the job. Use this to do something - # other than try the exact same job again. + # other than try the exact same job again # # @return [Array] new job arguments + # + # @api public def args_for_retry(*args) args end - # Convenience method to test whether you may retry on a given exception. + # Convenience method to test whether you may retry on a given + # exception # + # @param [Exception] an instance of Exception. Deprecated: can + # also be a Class + # # @return [Boolean] + # + # @api public def retry_exception?(exception) return true if retry_exceptions.nil? - !! retry_exceptions.any? { |ex| ex >= exception } + !! retry_exceptions.any? do |ex| + if exception.is_a?(Class) + ex >= exception + else + ex === exception + end + end end # @abstract - # Controls what exceptions may be retried. + # Controls what exceptions may be retried # # Default: `nil` - this will retry all exceptions. - # + # # @return [Array, nil] + # + # @api public def retry_exceptions if @retry_exceptions.is_a?(Hash) @retry_exceptions.keys else @retry_exceptions ||= nil end end - # Test if the retry criteria is valid. + # Test if the retry criteria is valid # # @param [Exception] exception # @param [Array] args job arguments # @return [Boolean] + # + # @api public def retry_criteria_valid?(exception, *args) # if the retry limit was reached, dont bother checking anything else. return false if retry_limit_reached? # We always want to retry if the exception matches. - should_retry = retry_exception?(exception.class) + should_retry = retry_exception?(exception) # call user retry criteria check blocks. retry_criteria_checks.each do |criteria_check| should_retry ||= !!instance_exec(exception, *args, &criteria_check) end should_retry end - # Retry criteria checks. + # Retry criteria checks # # @return [Array] + # + # @api public def retry_criteria_checks @retry_criteria_checks ||= [] - @retry_criteria_checks end - # Test if the retry limit has been reached. + # Test if the retry limit has been reached # # @return [Boolean] + # + # @api public def retry_limit_reached? if retry_limit == 0 true elsif retry_limit > 0 true if retry_attempt >= retry_limit @@ -179,11 +221,11 @@ false end end # Register a retry criteria check callback to be run before retrying - # the job again. + # the job again # # If any callback returns `true`, the job will be retried. # # @example Using a custom retry criteria check. # @@ -198,15 +240,19 @@ # # @yield [exception, *args] # @yieldparam exception [Exception] the exception that was raised # @yieldparam args [Array] job arguments # @yieldreturn [Boolean] false == dont retry, true = can retry + # + # @api public def retry_criteria_check(&block) retry_criteria_checks << block end - # Retries the job. + # Retries the job + # + # @api private def try_again(exception, *args) # 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 temp_retry_delay = ([-1, 1].include?(method(:retry_delay).arity) ? retry_delay(exception.class) : retry_delay) @@ -223,37 +269,43 @@ # sleep after requeue if enabled. sleep(sleep_after_requeue) if sleep_after_requeue > 0 end - # Resque before_perform hook. + # Resque before_perform hook # # Increments and sets the `@retry_attempt` count. + # + # @api private def before_perform_retry(*args) @on_failure_retry_hook_already_called = false # store number of retry attempts. retry_key = redis_retry_key(*args) Resque.redis.setnx(retry_key, -1) # default to -1 if not set. @retry_attempt = Resque.redis.incr(retry_key) # increment by 1. end - # Resque after_perform hook. + # Resque after_perform hook # # Deletes retry attempt count from Redis. + # + # @api private def after_perform_retry(*args) clean_retry_key(*args) end - # Resque on_failure hook. + # Resque on_failure hook # # Checks if our retry criteria is valid, if it is we try again. # Otherwise the retry attempt count is deleted from Redis. # - # NOTE: This hook will only allow execution once per job perform attempt. + # @note This hook will only allow execution once per job perform attempt. # This was added because Resque v1.20.0 calls the hook twice. # IMO; this isn't something resque-retry should have to worry about! + # + # @api private def on_failure_retry(exception, *args) return if @on_failure_retry_hook_already_called if retry_criteria_valid?(exception, *args) try_again(exception, *args) @@ -262,10 +314,15 @@ end @on_failure_retry_hook_already_called = true end + # Used to perform retry criteria check blocks under the job instance's context + # + # @return [Object] return value of the criteria check + # + # @api private def instance_exec(*args, &block) mname = "__instance_exec_#{Thread.current.object_id.abs}" class << self; self end.class_eval{ define_method(mname, &block) } begin ret = send(mname, *args) @@ -273,9 +330,12 @@ class << self; self end.class_eval{ undef_method(mname) } rescue nil end ret end + # Clean up retry state from redis once done + # + # @api private def clean_retry_key(*args) Resque.redis.del(redis_retry_key(*args)) end end