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

Instance Attribute Summary collapse

Instance Method Summary collapse

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 Attribute Details

#configObject (readonly) Originally defined in module Component

Returns the value of attribute config.

Instance Method Details

#default_tag(dir = Dir.pwd) ⇒ Object Originally defined in module Component

#fire_event(event, oneshot: true, reverse: false, reraise: false) ⇒ Object Originally defined in module Component

Invokes lifecycle hooks for event. Hooks run in registration order (or LIFO when reverse: true, used for teardown). A raise in one hook is reported via handle_exception and does NOT stop the next hook unless reraise: true (used in tests / fail-fast boot). oneshot: true clears the bucket after dispatch so the event can't fire twice.

#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

#hostnameObject Originally defined in module Component

#identityObject Originally defined in module Component

#leader?Boolean Originally defined in module Component

True iff this process currently holds the cluster dear-leader lock. Per spec, the check is performed at call time (Wurk does not cache); callers must not poll faster than the 60s follower cadence. Returns false unconditionally when WURK_LEADER=false (or SIDEKIQ_LEADER=false) is set on the process (opt-out hot-standby). Any Redis error is swallowed → false, so a transient partition can't propagate as an exception into user code.

Spec: docs/target/sidekiq-ent.md §6.1.

Returns:

  • (Boolean)

#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

#loggerObject Originally defined in module Component

--- delegated to config -------------------------------------------

#mono_msObject Originally defined in module Component

#process_nonceObject Originally defined in module Component

#real_msObject Originally defined in module Component

--- clocks ---------------------------------------------------------

#redisObject Originally defined in module Component

#safe_thread(name, priority: nil, &block) ⇒ Object Originally defined in module Component

Spawns a named thread that runs block under watchdog(name). The parent must retain the returned Thread; otherwise GC may not, but report_on_exception is disabled so we don't double-log on death.

#tidObject Originally defined in module Component

--- identity -------------------------------------------------------

#watchdog(last_words) ⇒ Object Originally defined in module Component

Wraps a block at a thread boundary: any unhandled exception is reported via handle_exception (so it lands in error_handlers / the log) and then re-raised. last_words is the component label included in the context.