Class: Revenant::Task
- Inherits:
-
Object
- Object
- Revenant::Task
- Defined in:
- lib/revenant/task.rb
Instance Attribute Summary collapse
- #logger ⇒ Object
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#options ⇒ Object
Returns the value of attribute options.
Instance Method Summary collapse
- #error(message) ⇒ Object
-
#initialize(name = nil) ⇒ Task
constructor
A new instance of Task.
-
#install_plugins ⇒ Object
Install any plugins that have registered themselves, or a custom list if the user has set it themselves.
-
#lock_function(&block) ⇒ Object
Set your own lock function.
-
#lock_module ⇒ Object
Returns a module that knows how to do some distributed locking.
-
#lock_type ⇒ Object
Used to pick the Task’s
lock_module
Particular lock types may offer various helpful features via this lock module. -
#lock_type=(val) ⇒ Object
Set a new lock type for this Task.
- #log(message) ⇒ Object
-
#method_missing(name, *args) ⇒ Object
Used to lazily store/retrieve options that may be needed by plugins.
-
#on_exit(&block) ⇒ Object
Code to run when the task is exiting.
-
#on_load(&block) ⇒ Object
Code to run just before the task looks for a lock This code runs after any necessary forks, and is therefore the proper place to open databases, logfiles, and any other resources you require.
-
#relock_every ⇒ Object
How many work loops to perform before re-acquiring the lock.
-
#relock_every=(loops) ⇒ Object
Set the frequency with which locks are re-acquired.
-
#restart_pending? ⇒ Boolean
At last, back to war.
-
#restart_soon ⇒ Object
Task will restart at the earliest safe opportunity after
restart_soon
is called. -
#run(&block) ⇒ Object
Takes actual block of code that is to be guarded by the lock.
-
#run_loop(&block) ⇒ Object
Run until we receive a shutdown/reload signal, or when the worker raises an Interrupt.
-
#shutdown ⇒ Object
Generally overridden when Revenant::Daemon is included The stack gets deeper here on every restart; this is here largely to ease testing.
-
#shutdown_pending? ⇒ Boolean
This could be the moment.
-
#shutdown_soon ⇒ Object
Task will shut down at the earliest safe opportunity after
shutdown_soon
is called. -
#sleep_for ⇒ Object
How many seconds to sleep after each work loop.
-
#sleep_for=(seconds) ⇒ Object
Set the number of seconds to sleep for after a work loop.
-
#startup ⇒ Object
Generally overridden when Revenant::Daemon is included.
Constructor Details
#initialize(name = nil) ⇒ Task
Returns a new instance of Task.
10 11 12 13 14 15 16 |
# File 'lib/revenant/task.rb', line 10 def initialize(name = nil) unless String === name || Symbol === name raise ArgumentError, "Usage: new(task_name)" end @name = name.to_sym @options = {} end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, *args) ⇒ Object
Used to lazily store/retrieve options that may be needed by plugins. We may want to capture, say, log_file
before actually loading the code that might care about such a concept.
170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/revenant/task.rb', line 170 def method_missing(name, *args) name = name.to_s last_char = name[-1,1] super(name, *args) unless last_char == "=" || last_char == "?" attr_name = name[0..-2].to_sym # :foo for 'foo=' or 'foo?' if last_char == "=" @options[attr_name] = args.at(0) else @options[attr_name] end end |
Instance Attribute Details
#logger ⇒ Object
190 191 192 |
# File 'lib/revenant/task.rb', line 190 def logger @logger ||= STDERR end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
6 7 8 |
# File 'lib/revenant/task.rb', line 6 def name @name end |
#options ⇒ Object
Returns the value of attribute options.
7 8 9 |
# File 'lib/revenant/task.rb', line 7 def @options end |
Instance Method Details
#error(message) ⇒ Object
186 187 188 |
# File 'lib/revenant/task.rb', line 186 def error() logger.puts "[#{$$}] #{Time.now.iso8601(2)} - ERROR: #{}" end |
#install_plugins ⇒ Object
Install any plugins that have registered themselves, or a custom list if the user has set it themselves.
196 197 198 199 200 |
# File 'lib/revenant/task.rb', line 196 def install_plugins ::Revenant.plugins.each do |name, plugin| plugin.install(self) end end |
#lock_function(&block) ⇒ Object
Set your own lock function. Will be called with a lock name as the arg. Should return true if a lock has been acquired, false otherwise. task.lock_function {|name| # .. }
91 92 93 94 95 96 97 |
# File 'lib/revenant/task.rb', line 91 def lock_function(&block) if block_given? @lock_function = block else @lock_function ||= lock_module.lock_function end end |
#lock_module ⇒ Object
Returns a module that knows how to do some distributed locking. May not be the code that actually performs the lock, if this Task has had a lock_function
assigned to it explicitly.
102 103 104 |
# File 'lib/revenant/task.rb', line 102 def lock_module ::Revenant.find_module(lock_type) end |
#lock_type ⇒ Object
Used to pick the Task’s lock_module
Particular lock types may offer various helpful features via this lock module. Defaults to :mysql
79 80 81 |
# File 'lib/revenant/task.rb', line 79 def lock_type @lock_type ||= :mysql end |
#lock_type=(val) ⇒ Object
Set a new lock type for this Task.
84 85 86 |
# File 'lib/revenant/task.rb', line 84 def lock_type=(val) @lock_type = val.to_sym end |
#log(message) ⇒ Object
182 183 184 |
# File 'lib/revenant/task.rb', line 182 def log() logger.puts "[#{$$}] #{Time.now.iso8601(2)} - #{}" end |
#on_exit(&block) ⇒ Object
Code to run when the task is exiting.
71 72 73 |
# File 'lib/revenant/task.rb', line 71 def on_exit(&block) @on_exit ||= block end |
#on_load(&block) ⇒ Object
Code to run just before the task looks for a lock This code runs after any necessary forks, and is therefore the proper place to open databases, logfiles, and any other resources you require.
66 67 68 |
# File 'lib/revenant/task.rb', line 66 def on_load(&block) @on_load ||= block end |
#relock_every ⇒ Object
How many work loops to perform before re-acquiring the lock. Defaults to 5. Setting it to 0 or nil will assume the lock is forever valid after acquisition.
110 111 112 |
# File 'lib/revenant/task.rb', line 110 def relock_every @relock_every ||= 5 end |
#relock_every=(loops) ⇒ Object
Set the frequency with which locks are re-acquired. Setting it to 0 or nil will assume the lock is forever valid after acquisition.
117 118 119 120 121 122 123 124 |
# File 'lib/revenant/task.rb', line 117 def relock_every=(loops) loops ||= 0 if Integer === loops && loops >= 0 @relock_every = loops else raise ArgumentError, "argument must be nil or an integer >= 0" end end |
#restart_pending? ⇒ Boolean
At last, back to war.
149 150 151 |
# File 'lib/revenant/task.rb', line 149 def restart_pending? @restart ||= false end |
#restart_soon ⇒ Object
Task will restart at the earliest safe opportunity after restart_soon
is called.
155 156 157 158 |
# File 'lib/revenant/task.rb', line 155 def restart_soon @restart = true @shutdown = true end |
#run(&block) ⇒ Object
Takes actual block of code that is to be guarded by the lock. The run_loop
method does the actual work.
If ‘daemon?’ is true, your code (including on_load
) will execute after a fork.
Make sure you don’t open files and sockets in the exiting parent process by mistake. Open them in code that is called via on_load
.
48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/revenant/task.rb', line 48 def run(&block) unless @work = block raise ArgumentError, "Usage: run { while_we_have_the_lock }" end @shutdown = false @restart = false install_plugins startup # typically daemonizes the process, can have various implementations on_load.call(self) if on_load run_loop(&@work) on_exit.call(self) if on_exit shutdown end |
#run_loop(&block) ⇒ Object
Run until we receive a shutdown/reload signal, or when the worker raises an Interrupt. Runs after a fork when Revenant::Daemon is enabled.
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/revenant/task.rb', line 205 def run_loop(&block) acquired = false begin until shutdown_pending? # The usual situation if relock_every != 0 i ||= 0 # 0 % anything is 0, so we always try to get the lock on the first loop. if (i %= relock_every) == 0 acquired = lock_function.call(@name) end else # With relock_every set to 0, only acquire the lock once. # Hope you're sure that lock beongs to you. acquired ||= lock_function.call(@name) i = 0 # no point in incrementing something we don't check. end yield if acquired # Sleep one second at a time so we can quickly respond to # shutdown requests. sleep_for.times do sleep(1) unless shutdown_pending? end i += 1 end # loop rescue ::Interrupt => ex log "shutting down after interrupt: #{ex.class} - #{ex.}" shutdown_soon # Always shut down from an Interrupt, even mid-restart. return rescue ::Exception => ex error "restarting after error: #{ex.class} - #{ex.}" error "backtrace: #{ex.backtrace.join("\n")}" restart_soon # Restart if we run into an exception. end # begin block end |
#shutdown ⇒ Object
Generally overridden when Revenant::Daemon is included The stack gets deeper here on every restart; this is here largely to ease testing. Implement your own plugin providing shutdown
if you want to make something serious that calls this code after a restart signal.
30 31 32 33 34 35 36 37 |
# File 'lib/revenant/task.rb', line 30 def shutdown if restart_pending? && @work log "#{name} is restarting" run(&@work) else log "#{name} is shutting down" end end |
#shutdown_pending? ⇒ Boolean
This could be the moment.
144 145 146 |
# File 'lib/revenant/task.rb', line 144 def shutdown_pending? @shutdown ||= false end |
#shutdown_soon ⇒ Object
Task will shut down at the earliest safe opportunity after shutdown_soon
is called.
162 163 164 165 |
# File 'lib/revenant/task.rb', line 162 def shutdown_soon @restart = false @shutdown = true end |
#sleep_for ⇒ Object
How many seconds to sleep after each work loop. When we don’t have the lock, how long to sleep before checking again. Default is 5 seconds.
129 130 131 |
# File 'lib/revenant/task.rb', line 129 def sleep_for @sleep_for ||= 5 end |
#sleep_for=(seconds) ⇒ Object
Set the number of seconds to sleep for after a work loop.
134 135 136 137 138 139 140 141 |
# File 'lib/revenant/task.rb', line 134 def sleep_for=(seconds) seconds ||= 0 if Integer === seconds && seconds >= 0 @sleep_for = seconds else raise ArgumentError, "argument must be nil or an integer >= 0" end end |
#startup ⇒ Object
Generally overridden when Revenant::Daemon is included
19 20 21 22 |
# File 'lib/revenant/task.rb', line 19 def startup log "#{name} is starting" trap("INT") { shutdown_soon } end |