lib/resque/plugins/retry.rb in resque-retry-0.0.5 vs lib/resque/plugins/retry.rb in resque-retry-0.0.6

- old
+ new

@@ -1,9 +1,8 @@ module Resque module Plugins - ## # If you want your job to retry on failure, simply extend your module/class # with this module: # # class DeliverWebHook # extend Resque::Plugins::Retry # allows 1 retry by default. @@ -32,11 +31,17 @@ # heavy_lifting # end # end # module Retry - ## + + # Copy retry criteria checks on inheritance. + 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 identifier, # you should consider doing this if your job arguments # are many/long or may not cleanly cleanly to strings. # # Builds an identifier using the job arguments. This identifier @@ -47,122 +52,159 @@ def 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 def redis_retry_key(*args) ['resque-retry', name, identifier(*args)].compact.join(":").gsub(/\s/, '') end - ## # Maximum number of retrys we can attempt to successfully perform the job. # A retry limit of 0 or below will retry forever. # # @return [Fixnum] def retry_limit @retry_limit ||= 1 end - ## # 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 def retry_attempt @retry_attempt ||= 0 end - ## # @abstract # Number of seconds to delay until the job is retried. # # @return [Number] number of seconds to delay def retry_delay @retry_delay ||= 0 end - ## # @abstract # Modify the arguments used to retry the job. Use this to do something # other than try the exact same job again. # # @return [Array] new job arguments def args_for_retry(*args) args end - ## # Convenience method to test whether you may retry on a given exception. # # @return [Boolean] def retry_exception?(exception) return true if retry_exceptions.nil? !! retry_exceptions.any? { |ex| ex >= exception } end - ## # @abstract # Controls what exceptions may be retried. # # Default: `nil` - this will retry all exceptions. # # @return [Array, nil] def retry_exceptions @retry_exceptions ||= nil end - ## # Test if the retry criteria is valid. # # @param [Exception] exception # @param [Array] args job arguments # @return [Boolean] def retry_criteria_valid?(exception, *args) - # FIXME: let people extend retry criteria, give them a chance to say no. + # 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) + + # call user retry criteria check blocks. + retry_criteria_checks.each do |criteria_check| + should_retry ||= !!criteria_check.call(exception, *args) + end + + should_retry + end + + # Retry criteria checks. + # + # @return [Array] + def retry_criteria_checks + @retry_criteria_checks ||= [] + @retry_criteria_checks + end + + # Test if the retry limit has been reached. + # + # @return [Boolean] + def retry_limit_reached? if retry_limit > 0 - return false if retry_attempt >= retry_limit + return true if retry_attempt >= retry_limit end - retry_exception?(exception.class) + false end - ## + # Register a retry criteria check callback to be run before retrying + # the job again. + # + # If any callback returns `true`, the job will be retried. + # + # @example Using a custom retry criteria check. + # + # retry_criteria_check do |exception, *args| + # if exception.message =~ /InvalidJobId/ + # # don't retry if we got passed a invalid job id. + # false + # else + # true + # end + # end + # + # @yield [exception, *args] + # @yieldparam exception [Exception] the exception that was raised + # @yieldparam args [Array] job arguments + # @yieldreturn [Boolean] false == dont retry, true = can retry + def retry_criteria_check(&block) + retry_criteria_checks << block + end + # Will retry the job. def try_again(*args) if retry_delay <= 0 # If the delay is 0, no point passing it through the scheduler Resque.enqueue(self, *args_for_retry(*args)) else Resque.enqueue_in(retry_delay, self, *args_for_retry(*args)) end end - ## # Resque before_perform hook. # # Increments and sets the `@retry_attempt` count. def before_perform_retry(*args) 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. # # Deletes retry attempt count from Redis. def after_perform_retry(*args) Resque.redis.del(redis_retry_key(*args)) end - ## # 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. def on_failure_retry(exception, *args) @@ -170,9 +212,9 @@ try_again(*args) else Resque.redis.del(redis_retry_key(*args)) end end - end + end end -end \ No newline at end of file +end