Class: Wurk::JobRetry

Inherits:
Object
  • Object
show all
Includes:
Component
Defined in:
lib/wurk/job_retry.rb

Overview

Owns the retry pipeline. When perform raises, JobRetry decides whether to reschedule (‘retry` ZSET, exponential backoff + jitter), drop, kill, or send to the morgue. Wire-compat sacred: error_message / error_class / retry_count / failed_at / retried_at / error_backtrace field names and encodings (base64 of zlib of JSON for the backtrace) match Sidekiq byte for byte — third-party gems and the dashboard read them directly.

Two entry points wrap the dispatch onion in Processor#dispatch:

* `global(jobstr, queue)` — outermost, no job instance required.
  Rescues `Exception` so pre-instantiation failures (const_get, reloader)
  still get a retry recorded. Re-raises `Handled` so the processor
  skips ACK logging.
* `local(jobinst, jobstr, queue)` — inner, runs after the worker is
  instantiated. Honors per-class `sidekiq_retry_in_block` /
  `sidekiq_retries_exhausted_block` (and the wrapped-class variants).
  Raises `Handled` after booking the retry so `global` does not double-
  process the failure.

Spec: docs/target/sidekiq-free.md §17.

Defined Under Namespace

Classes: Handled, Skip

Constant Summary collapse

DEFAULT_MAX_RETRY_ATTEMPTS =
25

Constants included from Component

Component::DEFAULT_THREAD_PRIORITY, Component::PROCESS_NONCE

Instance Attribute Summary

Attributes included from Component

#config

Instance Method Summary collapse

Methods included from Component

#default_tag, #fire_event, #hostname, #identity, #leader?, #logger, #mono_ms, #process_nonce, #real_ms, #redis, #safe_thread, #tid, #watchdog

Constructor Details

#initialize(capsule) ⇒ JobRetry

Returns a new instance of JobRetry.



40
41
42
43
44
45
# File 'lib/wurk/job_retry.rb', line 40

def initialize(capsule)
  @capsule = capsule
  @config = capsule
  @max_retries = inner_config_get(:max_retries) || DEFAULT_MAX_RETRY_ATTEMPTS
  @backtrace_cleaner = inner_config_get(:backtrace_cleaner)
end

Instance Method Details

#global(jobstr, queue) ⇒ Object

Outermost retry guard. Rescues ‘Exception` so const_get / reloader failures still get a retry. `Handled` is re-raised intact; `Shutdown` bubbles up so the swarm can drain.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/wurk/job_retry.rb', line 50

def global(jobstr, queue)
  yield
rescue Handled, Wurk::Shutdown
  raise
rescue Exception => e # rubocop:disable Lint/RescueException
  raise Wurk::Shutdown if exception_caused_by_shutdown?(e)

  msg = Wurk.load_json(jobstr)
  if msg['retry']
    process_retry(nil, msg, queue, e)
  else
    run_death_handlers(msg, e)
  end

  raise Handled
end

#handle_exception(ex, ctx = {}) ⇒ Object

Component’s ‘handle_exception` delegates to `config.handle_exception`. When initialized with a Capsule, that’s not defined directly; route through the underlying Configuration.



90
91
92
# File 'lib/wurk/job_retry.rb', line 90

def handle_exception(ex, ctx = {})
  inner_config.handle_exception(ex, ctx)
end

#local(jobinst, jobstr, queue) ⇒ Object

Per-job retry guard. Same rescue semantics as ‘global` but the worker instance is in hand, so per-class `sidekiq_retry_in_block` and `sidekiq_retries_exhausted_block` can run. Raises `Handled` to short- circuit `global`’s rescue.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/wurk/job_retry.rb', line 71

def local(jobinst, jobstr, queue)
  yield
rescue Handled, Wurk::Shutdown
  raise
rescue Exception => e # rubocop:disable Lint/RescueException
  raise Wurk::Shutdown if exception_caused_by_shutdown?(e)

  msg = Wurk.load_json(jobstr)
  msg['retry'] = jobinst.class.get_sidekiq_options['retry'] if msg['retry'].nil?

  raise e unless msg['retry']

  process_retry(jobinst, msg, queue, e)
  raise Handled
end