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_exceptionsandnew_optionsonto 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 + 1times. -
#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 |