From a824b346dabe9c24f63c6764d0f9629ff2daf9eb Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 10 Feb 2009 12:06:25 -0800 Subject: add hot_config_file config parameter This allows changing certain variables without restarting the master process or code reload. Currently, only the following variables are supported: @timeout, @nr_workers, @hot_config_file. Any other config changes will/should require re-executing the running binary. This config file is run through eval(); so it really users plenty of rope to hang themselves with. Of course, it requires valid Ruby syntax: ------------------------- 8< ------------------------ @nr_workers = 8 @timeout = 15 @hot_config_file = "/var/tmp/new_hot_config_file" ------------------------- 8< ------------------------ Lowering the timeout will trigger all existing workers to be gracefully stopped and restarted. This file is loaded at startup, and overrides any config settings that may already be loaded. --- bin/unicorn-hello-world | 1 + lib/unicorn.rb | 55 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/bin/unicorn-hello-world b/bin/unicorn-hello-world index 2aba773..8d77ea1 100755 --- a/bin/unicorn-hello-world +++ b/bin/unicorn-hello-world @@ -22,5 +22,6 @@ end # make sure this hash is the last statement, as this is eval-ed by unicorn { # :listeners => %w(0.0.0.0:8080 127.0.0.1:7701 /tmp/test.sock), + # :hot_config_file => "/tmp/hot_config", :app => HelloWorld.new, } diff --git a/lib/unicorn.rb b/lib/unicorn.rb index 8b15da7..f877686 100644 --- a/lib/unicorn.rb +++ b/lib/unicorn.rb @@ -37,6 +37,7 @@ module Unicorn :listeners => %w(0.0.0.0:8080), :logger => Logger.new(STDERR), :nr_workers => 1, + :hot_config_file => nil, :after_fork => lambda { |server, worker_nr| server.logger.info("worker=#{worker_nr} spawned pid=#{$$}") @@ -76,6 +77,7 @@ module Unicorn @start_ctx.merge!(options[:start_ctx]) if options[:start_ctx] @purgatory = [] # prevents objects in here from being GC-ed @rd_sig = @wr_sig = nil + load_hot_config! if @hot_config_file end # Runs the thing. Returns self so you can run join on it @@ -179,9 +181,16 @@ module Unicorn reexec @mode = :idle trap_deferred('USR2') - when 'HUP' # exec binary and exit - reexec - break + when 'HUP' + if @hot_config_file + load_hot_config! + @mode = :idle + trap_deferred('HUP') + redo # immediate reaping since we may have QUIT workers + else # exec binary and exit + reexec + break + end else logger.error "master process in unknown mode: #{@mode}, resetting" @mode = :idle @@ -488,5 +497,45 @@ module Unicorn nil end + # only do minimal validation, assume the user knows what they're doing + def load_hot_config! + log_pfx = "hot_config_file=#{@hot_config_file}" + begin + unless File.readable?(@hot_config_file) + logger.error "#{log_pfx} not readable" + return + end + hot_config = File.read(@hot_config_file) + nr_workers, timeout = @nr_workers, @timeout + eval(hot_config) + if Numeric === @timeout + if timeout != @timeout + logger.info "#{log_pfx} set: timeout=#{@timeout}" + if timeout > @timeout # we don't want to have to KILL them later + logger.info "restarting all workers because timeout got lowered" + kill_each_worker('QUIT') + end + end + else + logger.info "#{log_pfx} invalid: timeout=#{@timeout.inspect}" + @timeout = timeout + end + if Integer === @nr_workers + to_kill = nr_workers - @nr_workers + if to_kill != 0 + logger.info "#{log_pfx} set: nr_workers=#{@nr_workers}" + if to_kill > 0 + @workers.keys[0...to_kill].each { |pid| kill_worker('QUIT', pid) } + end + end + else + logger.info "#{log_pfx} invalid: nr_workers=#{@nr_workers.inspect}" + @nr_workers = nr_workers + end + rescue Object => e + logger.error "#{log_pfx} error: #{e.message}" + end + end + end end -- cgit v1.2.3-24-ge0c7