Module: Wurk::Component
- Included in:
- CLI, Wurk::Cron::Poller, Embedded, Fetcher::Reaper, Fetcher::Reliable, Heartbeat, History, JobRetry, Launcher, Manager, Metrics::QueueRollup, Metrics::Rollup, Processor, Scheduled::Enq, Scheduled::Poller, Scheduled::ReliableEnq, Swarm, Swarm::ChildBoot
- Defined in:
- lib/wurk/component.rb
Overview
Shared mixin for runtime components (Launcher, Manager, Processor, Fetcher, Scheduler, Cron). Wraps clock readings, identity, thread spawning, lifecycle event dispatch, and exception forwarding so each component stays single-purpose.
Host class must expose #config returning either a Wurk::Configuration
or a Wurk::Capsule — both duck-type the methods we delegate to.
Spec: docs/target/sidekiq-free.md §11 (Sidekiq::Component).
Constant Summary collapse
- DEFAULT_THREAD_PRIORITY =
-1
- PROCESS_NONCE =
Stable for the life of the process — survives fork (children inherit the same nonce). Identity differs across forks because Process.pid does.
SecureRandom.hex(6)
- LEADER_CACHE_TTL_MS =
leader?cache TTL — see the method doc below. 5_000
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Returns the value of attribute config.
Instance Method Summary collapse
- #default_tag(dir = Dir.pwd) ⇒ Object
-
#fire_event(event, oneshot: true, reverse: false, reraise: false) ⇒ Object
Invokes lifecycle hooks for
event. - #handle_exception(ex, ctx = {}) ⇒ Object
- #hostname ⇒ Object
- #identity ⇒ Object
-
#leader? ⇒ Boolean
True iff this process currently holds the cluster
dear-leaderlock. -
#logger ⇒ Object
--- delegated to config -------------------------------------------.
- #mono_ms ⇒ Object
- #process_nonce ⇒ Object
-
#real_ms ⇒ Object
--- clocks ---------------------------------------------------------.
- #redis ⇒ Object
-
#safe_thread(name, priority: nil, &block) ⇒ Object
Spawns a named thread that runs
blockunderwatchdog(name). -
#tid ⇒ Object
--- identity -------------------------------------------------------.
-
#watchdog(last_words) ⇒ Object
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.
Instance Attribute Details
#config ⇒ Object (readonly)
Returns the value of attribute config.
23 24 25 |
# File 'lib/wurk/component.rb', line 23 def config @config end |
Instance Method Details
#default_tag(dir = Dir.pwd) ⇒ Object
56 57 58 |
# File 'lib/wurk/component.rb', line 56 def default_tag(dir = Dir.pwd) File.basename(dir) end |
#fire_event(event, oneshot: true, reverse: false, reraise: false) ⇒ Object
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.
127 128 129 130 131 132 133 134 |
# File 'lib/wurk/component.rb', line 127 def fire_event(event, oneshot: true, reverse: false, reraise: false) bucket = config[:lifecycle_events][event] return if bucket.nil? || bucket.empty? iter = reverse ? bucket.reverse : bucket iter.each { |hook| run_lifecycle_hook(hook, event, reraise) } bucket.clear if oneshot end |
#handle_exception(ex, ctx = {}) ⇒ Object
70 71 72 |
# File 'lib/wurk/component.rb', line 70 def handle_exception(ex, ctx = {}) config.handle_exception(ex, ctx) end |
#hostname ⇒ Object
44 45 46 |
# File 'lib/wurk/component.rb', line 44 def hostname ENV['DYNO'] || Socket.gethostname end |
#identity ⇒ Object
52 53 54 |
# File 'lib/wurk/component.rb', line 52 def identity "#{hostname}:#{::Process.pid}:#{process_nonce}" end |
#leader? ⇒ Boolean
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.
87 88 89 90 91 92 93 94 95 96 |
# File 'lib/wurk/component.rb', line 87 def leader? return false if Wurk::Leader.opted_out? now = mono_ms if @leader_checked_at.nil? || (now - @leader_checked_at) >= LEADER_CACHE_TTL_MS @leader_checked_at = now @leader_cached = fetch_leader? end @leader_cached end |
#logger ⇒ Object
--- delegated to config -------------------------------------------
62 63 64 |
# File 'lib/wurk/component.rb', line 62 def logger config.logger end |
#mono_ms ⇒ Object
34 35 36 |
# File 'lib/wurk/component.rb', line 34 def mono_ms ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond) end |
#process_nonce ⇒ Object
48 49 50 |
# File 'lib/wurk/component.rb', line 48 def process_nonce PROCESS_NONCE end |
#real_ms ⇒ Object
--- clocks ---------------------------------------------------------
30 31 32 |
# File 'lib/wurk/component.rb', line 30 def real_ms ::Process.clock_gettime(::Process::CLOCK_REALTIME, :millisecond) end |
#redis ⇒ Object
66 67 68 |
# File 'lib/wurk/component.rb', line 66 def redis(&) config.redis(&) end |
#safe_thread(name, priority: nil, &block) ⇒ Object
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.
113 114 115 116 117 118 119 120 |
# File 'lib/wurk/component.rb', line 113 def safe_thread(name, priority: nil, &block) Thread.new do Thread.current.name = name Thread.current.priority = priority || DEFAULT_THREAD_PRIORITY Thread.current.report_on_exception = false watchdog(name, &block) end end |
#tid ⇒ Object
--- identity -------------------------------------------------------
40 41 42 |
# File 'lib/wurk/component.rb', line 40 def tid (Thread.current.object_id ^ ::Process.pid).to_s(36) end |
#watchdog(last_words) ⇒ Object
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.
103 104 105 106 107 108 |
# File 'lib/wurk/component.rb', line 103 def watchdog(last_words) yield rescue StandardError => e handle_exception(e, { context: last_words }) raise end |