Module: Wurk::Component
- Included in:
- CLI, Wurk::Cron::Poller, Embedded, Fetcher::Reaper, Fetcher::Reliable, Heartbeat, JobRetry, Launcher, Manager, Metrics::Rollup, Processor, Scheduled::Enq, Scheduled::Poller, Swarm
- 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)
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-leader` lock.
-
#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 ‘block` under `watchdog(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
53 54 55 |
# File 'lib/wurk/component.rb', line 53 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.
118 119 120 121 122 123 124 125 |
# File 'lib/wurk/component.rb', line 118 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
67 68 69 |
# File 'lib/wurk/component.rb', line 67 def handle_exception(ex, ctx = {}) config.handle_exception(ex, ctx) end |
#hostname ⇒ Object
41 42 43 |
# File 'lib/wurk/component.rb', line 41 def hostname ENV['DYNO'] || Socket.gethostname end |
#identity ⇒ Object
49 50 51 |
# File 'lib/wurk/component.rb', line 49 def identity "#{hostname}:#{::Process.pid}:#{process_nonce}" end |
#leader? ⇒ Boolean
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` 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.
81 82 83 84 85 86 87 |
# File 'lib/wurk/component.rb', line 81 def leader? return false if ENV[Wurk::Leader::OPT_OUT_ENV].to_s.downcase == 'false' redis { |c| c.call('GET', Wurk::Leader::DEFAULT_KEY) } == identity rescue StandardError false end |
#logger ⇒ Object
— delegated to config ——————————————-
59 60 61 |
# File 'lib/wurk/component.rb', line 59 def logger config.logger end |
#mono_ms ⇒ Object
31 32 33 |
# File 'lib/wurk/component.rb', line 31 def mono_ms ::Process.clock_gettime(::Process::CLOCK_MONOTONIC, :millisecond) end |
#process_nonce ⇒ Object
45 46 47 |
# File 'lib/wurk/component.rb', line 45 def process_nonce PROCESS_NONCE end |
#real_ms ⇒ Object
— clocks ———————————————————
27 28 29 |
# File 'lib/wurk/component.rb', line 27 def real_ms ::Process.clock_gettime(::Process::CLOCK_REALTIME, :millisecond) end |
#redis ⇒ Object
63 64 65 |
# File 'lib/wurk/component.rb', line 63 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.
104 105 106 107 108 109 110 111 |
# File 'lib/wurk/component.rb', line 104 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 ——————————————————-
37 38 39 |
# File 'lib/wurk/component.rb', line 37 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.
94 95 96 97 98 99 |
# File 'lib/wurk/component.rb', line 94 def watchdog(last_words) yield rescue StandardError => e handle_exception(e, { context: last_words }) raise end |