Module: Wurk::Sentry::RetryPolicy
- Defined in:
- lib/wurk/sentry/retry_policy.rb
Overview
Answers one question: "if this job raises right now, is that the end of the road?" Only terminal failures are worth an alert — reporting every attempt turns a single flaky job into 25 Sentry events over ~21 days.
The prediction has to be made ahead of JobRetry, because the
server middleware chain runs inside JobRetry#local (see
Processor#dispatch): the middleware's rescue fires before the retry
layer has touched the payload. So at rescue time retry_count is still
the number of retries already performed, and this class re-derives
what JobRetry#process_retry is about to conclude:
* `bump_retry_count` sets `retry_count = 0` on the first failure and
`retry_count += 1` thereafter — so the post-bump count is
`retry_count.nil? ? 0 : retry_count + 1`.
* `exhausted?` kills the job once that count reaches `max_attempts`.
Which makes the last attempt the one that arrives with
retry_count == max_attempts - 1 (and, for retry: 0 / retry: false,
the very first one).
Not predictable from the payload — and therefore reported one attempt
late, or not at all: a sidekiq_retry_in block returning :discard or
:kill. Those are host decisions made after this point.
Class Method Summary collapse
-
.configured_max_retries(config) ⇒ Object
configis whatever the Chain bound to the middleware — a Capsule in a running worker, a Configuration in tests. - .max_attempts(retry_option, config) ⇒ Object
- .next_retry_count(job) ⇒ Object
- .retry_for_elapsed?(job) ⇒ Boolean
-
.retry_option_for(job, instance) ⇒ Object
Mirrors
JobRetry#local: a payload with noretrykey falls back to the worker class's ownsidekiq_options. - .terminal?(job, instance = nil, config = nil) ⇒ Boolean
-
.time_for(value) ⇒ Object
Wire format matches JobRetry#time_for: Float seconds or Integer millis.
Class Method Details
.configured_max_retries(config) ⇒ Object
config is whatever the Chain bound to the middleware — a Capsule in a
running worker, a Configuration in tests. Only the latter has [].
66 67 68 69 70 71 |
# File 'lib/wurk/sentry/retry_policy.rb', line 66 def configured_max_retries(config) return nil if config.nil? inner = config.respond_to?(:config) ? config.config : config inner.respond_to?(:[]) ? inner[:max_retries] : nil end |
.max_attempts(retry_option, config) ⇒ Object
58 59 60 61 62 |
# File 'lib/wurk/sentry/retry_policy.rb', line 58 def max_attempts(retry_option, config) return retry_option if retry_option.is_a?(::Integer) configured_max_retries(config) || Wurk::JobRetry::DEFAULT_MAX_RETRY_ATTEMPTS end |
.next_retry_count(job) ⇒ Object
53 54 55 56 |
# File 'lib/wurk/sentry/retry_policy.rb', line 53 def next_retry_count(job) count = job['retry_count'] count ? count + 1 : 0 end |
.retry_for_elapsed?(job) ⇒ Boolean
73 74 75 76 77 78 79 80 |
# File 'lib/wurk/sentry/retry_policy.rb', line 73 def retry_for_elapsed?(job) failed_at = job['failed_at'] # First failure: JobRetry stamps `failed_at = now` and then finds the # budget un-spent, so the job always gets at least one retry. return false unless failed_at time_for(failed_at) + job['retry_for'] < ::Time.now end |
.retry_option_for(job, instance) ⇒ Object
Mirrors JobRetry#local: a payload with no retry key falls back to
the worker class's own sidekiq_options.
45 46 47 48 49 50 51 |
# File 'lib/wurk/sentry/retry_policy.rb', line 45 def retry_option_for(job, instance) value = job['retry'] return value unless value.nil? klass = instance&.class klass.respond_to?(:get_sidekiq_options) ? klass.['retry'] : true end |
.terminal?(job, instance = nil, config = nil) ⇒ Boolean
33 34 35 36 37 38 39 40 41 |
# File 'lib/wurk/sentry/retry_policy.rb', line 33 def terminal?(job, instance = nil, config = nil) retry_option = retry_option_for(job, instance) return true unless retry_option # `retry_for` is a wall-clock budget that supersedes the attempt count # entirely (JobRetry#exhausted? branches on it before looking at max). return retry_for_elapsed?(job) if job['retry_for'] next_retry_count(job) >= max_attempts(retry_option, config) end |
.time_for(value) ⇒ Object
Wire format matches JobRetry#time_for: Float seconds or Integer millis.
83 84 85 86 87 |
# File 'lib/wurk/sentry/retry_policy.rb', line 83 def time_for(value) return ::Time.at(value) if value.is_a?(::Float) ::Time.at(value / 1000, value % 1000, :millisecond) end |