Class: Wurk::Processor

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

Overview

Inside each Manager, N Processors run in parallel. Each owns one thread, pulls a UnitOfWork from the capsule's fetcher, parses the payload, walks the server middleware chain, invokes perform, then ACKs (retires the payload from the per-process private list). The ACK is handed to the fetcher, which pipelines it with the next fetch rather than spending a round trip on it — #flush_acks covers the case where there is no next fetch.

Shutdown is two-stage:

* `terminate` flips a flag; the run loop exits between jobs.
* `kill` additionally raises `Wurk::Shutdown` into the thread so an
in-flight perform unwinds. The current UoW is NOT acked, so the
payload survives in the private list and is reclaimed on next boot.

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

Defined Under Namespace

Classes: Counter, SharedWorkState

Constant Summary collapse

PROCESSED =
Counter.new
FAILURE =
Counter.new
EXPIRED =
Counter.new
WORK_STATE =
SharedWorkState.new

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(capsule, &callback) ⇒ Processor

Returns a new instance of Processor.



66
67
68
69
70
71
72
73
74
75
76
# File 'lib/wurk/processor.rb', line 66

def initialize(capsule, &callback)
  @capsule = capsule
  @config = capsule
  @callback = callback
  @done = false
  @job = nil
  @thread = nil
  @reloader = resolve_reloader(capsule.config[:reloader])
  @job_logger = (capsule.config[:job_logger] || JobLogger).new(capsule.config)
  @retrier = JobRetry.new(capsule)
end

Instance Attribute Details

#capsule ⇒ Object (readonly)

Returns the value of attribute capsule.



64
65
66
# File 'lib/wurk/processor.rb', line 64

def capsule
  @capsule
end

#config ⇒ Object (readonly) Originally defined in module Component

Returns the value of attribute config.

#job ⇒ Object (readonly)

Returns the value of attribute job.



64
65
66
# File 'lib/wurk/processor.rb', line 64

def job
  @job
end

#thread ⇒ Object (readonly)

Returns the value of attribute thread.



64
65
66
# File 'lib/wurk/processor.rb', line 64

def thread
  @thread
end

Class Method Details

.vm_exiting? ⇒ Boolean

Returns:

  • (Boolean)


186
# File 'lib/wurk/processor.rb', line 186

def self.vm_exiting? = @vm_exiting

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.

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

Capsule doesn't define handle_exception (it's a Configuration method); override Component's delegation so error handlers fire.



108
109
110
# File 'lib/wurk/processor.rb', line 108

def handle_exception(ex, ctx = {})
  @capsule.config.handle_exception(ex, ctx)
end

#hostname ⇒ Object Originally defined in module Component

#identity ⇒ Object Originally defined in module Component

#kill(wait = false) ⇒ Object

Hard-stop: flips the flag and unwinds the in-flight job by raising Wurk::Shutdown into the worker thread. The UoW is intentionally not acked — the payload remains in the private list and is reclaimed on next boot via Reliable#bulk_requeue.



90
91
92
93
94
95
96
# File 'lib/wurk/processor.rb', line 90

def kill(wait = false) # rubocop:disable Style/OptionalBooleanParameter
  @done = true
  return if @thread.nil?

  @thread.raise ::Wurk::Shutdown
  @thread.value if wait
end

#leader? ⇒ Boolean Originally defined in module Component

True iff this process currently holds the cluster dear-leader lock. Cached per Component instance for LEADER_CACHE_TTL_MS (~5s): cron and the metrics rollups call this every tick, and an uncached GET would double their Redis traffic at short intervals for no benefit — the lock's own renewal cadence (60s+, spec §6.1) easily tolerates a few-second-stale read. 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)

#logger ⇒ Object Originally defined in module Component

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

#mono_ms ⇒ Object Originally defined in module Component

#process_nonce ⇒ Object Originally defined in module Component

#process_one ⇒ Object

Single iteration: fetch one UoW, process it. Public so tests can drive the loop step-by-step without spawning a thread.



114
115
116
117
118
# File 'lib/wurk/processor.rb', line 114

def process_one
  @job = fetch
  process(@job) if @job
  @job = nil
end

#real_ms ⇒ Object Originally defined in module Component

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

#redis(idempotent: false) ⇒ Object 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.

Priority resolution matches Sidekiq (component.rb:44-48): explicit argument, then config.thread_priority, then -1. Ruby's default of 0 buys a 100ms timeslice; each negative step halves it, so -1 keeps a CPU-heavy capsule from starving its siblings for a whole tick.

#start ⇒ Object



102
103
104
# File 'lib/wurk/processor.rb', line 102

def start
  @thread ||= safe_thread("#{@capsule.name}/processor", &method(:run)) # rubocop:disable Naming/MemoizedInstanceVariableName
end

#stopping? ⇒ Boolean

Returns:

  • (Boolean)


98
99
100
# File 'lib/wurk/processor.rb', line 98

def stopping?
  @done
end

#terminate(wait = false) ⇒ Object

Sidekiq surface — positional boolean to match the drop-in contract.



79
80
81
82
83
84
# File 'lib/wurk/processor.rb', line 79

def terminate(wait = false) # rubocop:disable Style/OptionalBooleanParameter
  @done = true
  return if @thread.nil?

  @thread.value if wait
end

#tid ⇒ Object Originally defined in module Component

#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.