Class: CMDx::Retry
- Inherits:
-
Object
- Object
- CMDx::Retry
- Defined in:
- lib/cmdx/retry.rb
Overview
Configurable retry-on-exception wrapper around a task’s ‘work`. Supports exception list, attempt `:limit`, base `:delay`, `:max_delay` cap, and `:jitter` strategy (symbol, proc, or a configured block). Declared via `Task.retry_on` and accumulates across inheritance.
Instance Attribute Summary collapse
-
#exceptions ⇒ Object
readonly
Returns the value of attribute exceptions.
Instance Method Summary collapse
-
#build(new_exceptions, new_options, &block) {|attempt, delay, prev_delay| ... } ⇒ Retry
Returns a new Retry layering ‘new_exceptions` and `new_options` onto the current one.
-
#delay ⇒ Float
Base delay in seconds.
-
#initialize(exceptions, options = EMPTY_HASH, &block) {|attempt, delay, prev_delay| ... } ⇒ Retry
constructor
A new instance of Retry.
-
#jitter ⇒ Symbol, ...
Jitter strategy or the block given to #initialize.
- #limit ⇒ Integer
-
#max_delay ⇒ Float?
Upper bound for computed delays.
-
#process(task = nil) {|attempt| ... } ⇒ Object
Executes the block up to ‘limit + 1` times.
-
#wait(attempt, task = nil, prev_delay = nil) ⇒ Float?
Sleeps ‘attempt`’s jittered/bounded delay.
Constructor Details
#initialize(exceptions, options = EMPTY_HASH, &block) {|attempt, delay, prev_delay| ... } ⇒ Retry
Returns a new instance of Retry.
24 25 26 27 28 |
# File 'lib/cmdx/retry.rb', line 24 def initialize(exceptions, = EMPTY_HASH, &block) @exceptions = exceptions.flatten @options = .freeze @block = block end |
Instance Attribute Details
#exceptions ⇒ Object (readonly)
Returns the value of attribute exceptions.
10 11 12 |
# File 'lib/cmdx/retry.rb', line 10 def exceptions @exceptions end |
Instance Method Details
#build(new_exceptions, new_options, &block) {|attempt, delay, prev_delay| ... } ⇒ Retry
Returns a new Retry layering ‘new_exceptions` and `new_options` onto the current one. Used for inheritance so subclasses extend rather than replace. Returns `self` only when every override (exceptions, options, and block) is empty so option-only updates such as `retry_on(limit: 5)` still take effect.
41 42 43 44 45 46 47 48 |
# File 'lib/cmdx/retry.rb', line 41 def build(new_exceptions, , &block) return self if new_exceptions.empty? && .empty? && block.nil? merged_exceptions = exceptions | new_exceptions.flatten = @options.merge() self.class.new(merged_exceptions, , &block || @block) end |
#delay ⇒ Float
Returns base delay in seconds.
56 57 58 |
# File 'lib/cmdx/retry.rb', line 56 def delay @options[:delay] || 0.5 end |
#jitter ⇒ Symbol, ...
Returns jitter strategy or the block given to #initialize.
66 67 68 |
# File 'lib/cmdx/retry.rb', line 66 def jitter @options[:jitter] || @block end |
#limit ⇒ Integer
51 52 53 |
# File 'lib/cmdx/retry.rb', line 51 def limit @options[:limit] || 3 end |
#max_delay ⇒ Float?
Returns upper bound for computed delays.
61 62 63 |
# File 'lib/cmdx/retry.rb', line 61 def max_delay @options[:max_delay] end |
#process(task = nil) {|attempt| ... } ⇒ Object
Executes the block up to ‘limit + 1` times. Re-raises the last exception when attempts are exhausted.
127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/cmdx/retry.rb', line 127 def process(task = nil, &) return yield(0) if exceptions.empty? || !limit.positive? prev_delay = nil (limit + 1).times do |attempt| return yield(attempt) rescue *exceptions => e raise(e) if attempt >= limit raise(e) unless Util.satisfied?(@options[:if], @options[:unless], task, e, attempt) prev_delay = wait(attempt, task, prev_delay) end end |
#wait(attempt, task = nil, prev_delay = nil) ⇒ Float?
Sleeps ‘attempt`’s jittered/bounded delay. No-op when the base delay is zero.
Custom jitter callables (registry, task Symbol method, ‘Proc` / block via `instance_exec` on the task, and other `#call`-ables) always receive `(attempt, delay, prev_delay)` so strategies share one shape; ignore `prev_delay` when you do not need decorrelated threading.
Non-numeric or non-finite jitter results are sanitized to the base ‘delay` and the final sleep is always clamped to `[0, max_delay]` when `max_delay` is set, preventing self-DoS from a buggy jitter returning `Float::INFINITY` or a non-Numeric value.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/cmdx/retry.rb', line 88 def wait(attempt, task = nil, prev_delay = nil) return unless delay.positive? d = case jitter when NilClass delay when Symbol registry = retriers_registry(task) if registry.key?(jitter) registry.lookup(jitter).call(attempt, delay, prev_delay) else task.send(jitter, attempt, delay, prev_delay) end when Proc task.instance_exec(attempt, delay, prev_delay, &jitter) else if jitter.respond_to?(:call) jitter.call(attempt, delay, prev_delay) else delay end end d = delay unless d.is_a?(Numeric) && d.finite? d = d.clamp(0, max_delay) if max_delay Kernel.sleep(d) if d.positive? d end |