class Sleepier::Supervisor
`Sleepier::Supervisor` manages a set of `Sleepier::ChildSpec` objects according to the guidance passed to it via the constructor.
Constants
- VALID_RESTART_STRATEGIES
@todo implement strategies other than :one_for_one
Determines how to handle restarts for all processes supervised
-
:one_for_one - Only restart the process that failed if one process terminates
-
:one_for_all - Restart all processes if one process terminates
-
:rest_for_one - Restart all processes after the process, in the order they started, that terminates as well as the process that terminated
-
Public Class Methods
Create the supervisor. Does not start it.
@param child_specs [Sleepier::ChildSpec] What processes to start and monitor @param restart_strategy [VALID_RESTART_STRATEGIES] Managing how restarts are handled for the group of processes @param max_restart_count [int] How many times within `max_restart_window` a process can restart. @param max_restart_window [int] A moving window in seconds during which a process may terminate `max_restart_count` times before the supervisor gives up.
# File lib/sleepier.rb, line 170 def initialize(child_specs, restart_strategy, max_restart_count=3, max_restart_window=5) @child_specs = Hash.new child_specs.each {|child_spec| @child_specs[child_spec.child_id] = child_spec} @max_restart_count = max_restart_count @max_restart_window = max_restart_window if VALID_RESTART_STRATEGIES.include?(restart_strategy) @restart_strategy = restart_strategy else raise Exception.new('Invalid restart strategy') end @started = false end
Public Instance Methods
Internal function that handles a process finishing
@param pid [int] The pid of the finished process, used to find the right child process @param status [int] Status code. 0 is normal, anything else is abnormal termination
@return [true,false] Returns true if the process should have been restarted and was, false otherwise
# File lib/sleepier.rb, line 250 def handle_finished_process(pid, status) @child_specs.each do |child_id, child_spec| if child_spec.pid == pid if child_spec.should_restart?(status, @max_restart_count, @max_restart_window) child_spec.restarted self.start_process(child_id) return true else Sleepier.logger.info("#{child_spec.restart.to_s.capitalize} child #{child_spec.child_id} finished. Will not be restarted") return false end end end end
Watches for processes to terminate. Sends the pid and status returned by the process to `handle_finished_process`
@note This may be called before calling start to minimize chances of a process terminating before monitoring starts
# File lib/sleepier.rb, line 189 def monitor while true begin pid, status = Process.wait2 rescue Errno::ECHILD if @started Sleepier.logger.warn("No children, exiting") break end end self.handle_finished_process(pid, status) end end
Start all the child processes
# File lib/sleepier.rb, line 205 def start @child_specs.each do |child_id, child_spec| self.start_process(child_id) end @started = true end
Add a new child process and start it
@param child_spec [Sleepier::ChildSpec] spec to use and start
# File lib/sleepier.rb, line 215 def start_new_child(child_spec) @child_specs[child_spec.child_id] = child_spec self.start_process(child_spec.child_id) end
Internal function used by `start_new_child` and `start`
# File lib/sleepier.rb, line 266 def start_process(child_id) child_spec = @child_specs[child_id] pid = Process.fork do child_spec.start_func.call(*(child_spec.args)) end child_spec.pid = pid Sleepier.logger.info("Started #{child_spec.child_id} with pid #{pid}") end
Starts termination of a process. This does not wait for the process to finish.
@param child_id Which child to terminate
@todo Add a callback for when the process finishes here
# File lib/sleepier.rb, line 225 def terminate_child(child_id) child_spec = @child_specs[child_id] child_spec.terminating = true case child_spec.shutdown when :brutal_kill Process.kill("KILL", child_spec.pid) when :timeout Process.kill("TERM", child_spec.pid) Thread.new do sleep(child_spec.shutdown_timeout) Process.kill("KILL", child_spec.pid) end when :infinity Process.kill("TERM", child_spec.pid) end end