about summary refs log tree commit homepage
path: root/lib
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2009-02-10 12:06:25 -0800
committerEric Wong <normalperson@yhbt.net>2009-02-10 12:23:15 -0800
commita824b346dabe9c24f63c6764d0f9629ff2daf9eb (patch)
tree94b6cd0abc52dc31b5a535091373831fca0675f2 /lib
parent36573963a235fd1016ce978667ab83eb40011061 (diff)
downloadunicorn-a824b346dabe9c24f63c6764d0f9629ff2daf9eb.tar.gz
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.
Diffstat (limited to 'lib')
-rw-r--r--lib/unicorn.rb55
1 files changed, 52 insertions, 3 deletions
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