class Sidekiq::ProcessManager::Manager
Attributes
cli[R]
Public Class Methods
new(process_count: 1, prefork: false, preboot: nil, mode: nil, silent: false)
click to toggle source
# File lib/sidekiq/process_manager/manager.rb, line 10 def initialize(process_count: 1, prefork: false, preboot: nil, mode: nil, silent: false) require "sidekiq/cli" # Get the number of processes to fork @process_count = process_count raise ArgumentError.new("Process count must be greater than 1") if @process_count < 1 @prefork = (prefork && process_count > 1) @preboot = preboot if process_count > 1 && !prefork if mode == :testing require_relative "../../../spec/support/mocks" @cli = MockSidekiqCLI.new(silent) else @cli = Sidekiq::CLI.instance end @silent = silent @pids = [] @terminated_pids = [] @started = false @monitor = Monitor.new end
Public Instance Methods
pids()
click to toggle source
# File lib/sidekiq/process_manager/manager.rb, line 94 def pids @pids.dup end
start()
click to toggle source
Start the process manager. This method will start the specified number of sidekiq processes and monitor them. It will only exit once all child processes have exited. If a child process dies unexpectedly, it will be restarted.
Child processes are manged by sending the signals you would normally send to a sidekiq process to the process manager instead.
# File lib/sidekiq/process_manager/manager.rb, line 41 def start raise "Process manager already started" if started? @started = true load_sidekiq master_pid = ::Process.pid # Trap signals that will be forwarded to child processes [:INT, :TERM, :USR1, :USR2, :TSTP, :TTIN].each do |signal| ::Signal.trap(signal) do if ::Process.pid == master_pid send_signal_to_children(signal) end end end # Ensure that child processes receive the term signal when the master process exits. at_exit do if ::Process.pid == master_pid && @process_count > 0 @pids.each do |pid| send_signal_to_children(:TERM) end end end @process_count.times do start_child_process! end log_info("Process manager started with pid #{::Process.pid}") monitor_child_processes log_info("Process manager #{::Process.pid} exiting") end
started?()
click to toggle source
# File lib/sidekiq/process_manager/manager.rb, line 98 def started? @started end
stop()
click to toggle source
Helper to gracefully stop all child processes.
# File lib/sidekiq/process_manager/manager.rb, line 88 def stop @process_count = 0 send_signal_to_children(:TSTP) send_signal_to_children(:TERM) end
wait(timeout = 5)
click to toggle source
Helper to wait on the manager to wait on child processes to start up.
# File lib/sidekiq/process_manager/manager.rb, line 77 def wait(timeout = 5) start_time = Time.now while Time.now < start_time + timeout return if @pids.size == @process_count sleep(0.01) end raise Timeout::Error.new("child processes failed to start in #{timeout} seconds") end
Private Instance Methods
load_sidekiq()
click to toggle source
# File lib/sidekiq/process_manager/manager.rb, line 124 def load_sidekiq @cli.parse Sidekiq.options[:daemon] = false Sidekiq.options[:pidfile] = false if @prefork log_info("Pre-forking application") # Set $0 so instrumentation libraries detecting sidekiq from the command run will work properly. save_command_line = $0 $0 = File.join(File.dirname($0), "sidekiq") # Prior to sidekiq 6.1 the method to boot the application was boot_system if @cli.methods.include?(:boot_application) || @cli.private_methods.include?(:boot_application) @cli.send(:boot_application) else @cli.send(:boot_system) end $0 = save_command_line Sidekiq::ProcessManager.run_before_fork_hooks elsif @preboot && !@preboot.empty? if ::File.exist?(@preboot) require ::File.expand_path(@preboot).sub(/\.rb\Z/, "") else log_warning("Could not find preboot file #{@preboot}") end end end
log_info(message)
click to toggle source
# File lib/sidekiq/process_manager/manager.rb, line 104 def log_info(message) return if @silent if $stdout.tty? $stdout.write("#{message}#{$/}") $stdout.flush else Sidekiq.logger.info(message) end end
log_warning(message)
click to toggle source
# File lib/sidekiq/process_manager/manager.rb, line 114 def log_warning(message) return if @silent if $stderr.tty? $stderr.write("#{message}#{$/}") $stderr.flush else Sidekiq.logger.warn(message) end end
monitor_child_processes()
click to toggle source
Listen for child processes dying and restart if necessary.
# File lib/sidekiq/process_manager/manager.rb, line 181 def monitor_child_processes loop do pid = ::Process.wait @pids.delete(pid) log_info("Sidekiq process #{pid} exited") # If there are not enough processes running, start a replacement one. if @process_count > @pids.size start_child_process! if @pids.size < @process_count end set_program_name! if @pids.empty? break end end end
send_signal_to_children(signal)
click to toggle source
# File lib/sidekiq/process_manager/manager.rb, line 167 def send_signal_to_children(signal) log_info("Process manager trapped signal #{signal}") @process_count = 0 if signal == :INT || signal == :TERM @pids.each do |pid| begin log_info("Sending signal #{signal} to sidekiq process #{pid}") ::Process.kill(signal, pid) rescue => e log_warning("Error sending signal #{signal} to sidekiq process #{pid}: #{e.inspect}") end end end
set_program_name!()
click to toggle source
# File lib/sidekiq/process_manager/manager.rb, line 150 def set_program_name! $PROGRAM_NAME = "sidekiq process manager #{Sidekiq.options[:tag]} [#{@pids.size} processes]" end
start_child_process!()
click to toggle source
# File lib/sidekiq/process_manager/manager.rb, line 154 def start_child_process! @pids << fork do # Set $0 so instrumentation libraries detecting sidekiq from the command run will work properly. $0 = File.join(File.dirname($0), "sidekiq") @process_count = 0 @pids.clear Sidekiq::ProcessManager.run_after_fork_hooks @cli.run end log_info("Forked sidekiq process with pid #{@pids.last}") set_program_name! end