diff options
author | Eric Wong <normalperson@yhbt.net> | 2010-07-13 08:57:37 +0000 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2010-07-13 08:57:37 +0000 |
commit | c13bec3449396b21795966101367838161612d61 (patch) | |
tree | d2c925ccc1057677bb05ed27f899a53a196f2590 | |
parent | 3f0f9d6d72cf17b34c130b86eb933bbc513b24b3 (diff) | |
download | unicorn-c13bec3449396b21795966101367838161612d61.tar.gz |
As described in our SIGNALS documentation, sending SIGHUP to the old master (to respawn SIGWINCH-ed children) while the new master (spawned from SIGUSR2) is active is useful for backing out of an upgrade before sending SIGQUIT to the new master. Unfortunately, the SIGHUP signal to the old master will cause the ".oldbin" pid file to be reset to the non-".oldbin" version and thus attempt to clobber the pid file in use by the to-be-terminated new master process. Thanks to the previous commit to prevent redaemonization in the new master, the old master can reliably detect if the new master is active while it is reloading the config file. Thanks to Lawrence Pit for discovering this bug. ref: http://mid.gmane.org/4C3BEACF.7040301@gmail.com
-rw-r--r-- | lib/unicorn.rb | 5 | ||||
-rw-r--r-- | t/pid.ru | 3 | ||||
-rwxr-xr-x | t/t0008-back_out_of_upgrade.sh | 110 |
3 files changed, 118 insertions, 0 deletions
diff --git a/lib/unicorn.rb b/lib/unicorn.rb index 9e2d1c6..c231a4d 100644 --- a/lib/unicorn.rb +++ b/lib/unicorn.rb @@ -313,6 +313,11 @@ module Unicorn if path if x = valid_pid?(path) return path if pid && path == pid && x == $$ + if x == reexec_pid && pid =~ /\.oldbin\z/ + logger.warn("will not set pid=#{path} while reexec-ed "\ + "child is running PID:#{x}") + return + end raise ArgumentError, "Already running on PID:#{x} " \ "(or pid=#{path} is stale)" end diff --git a/t/pid.ru b/t/pid.ru new file mode 100644 index 0000000..f5fd31f --- /dev/null +++ b/t/pid.ru @@ -0,0 +1,3 @@ +use Rack::ContentLength +use Rack::ContentType, "text/plain" +run lambda { |env| [ 200, {}, [ "#$$\n" ] ] } diff --git a/t/t0008-back_out_of_upgrade.sh b/t/t0008-back_out_of_upgrade.sh new file mode 100755 index 0000000..96d4057 --- /dev/null +++ b/t/t0008-back_out_of_upgrade.sh @@ -0,0 +1,110 @@ +#!/bin/sh +. ./test-lib.sh +t_plan 13 "backout of USR2 upgrade" + +worker_wait_start () { + test xSTART = x"$(cat $fifo)" + unicorn_pid=$(cat $pid) +} + +t_begin "setup and start" && { + unicorn_setup + rm -f $pid.oldbin + +cat >> $unicorn_config <<EOF +after_fork do |server, worker| + # test script will block while reading from $fifo, + # so notify the script on the first worker we spawn + # by opening the FIFO + if worker.nr == 0 + File.open("$fifo", "wb") { |fp| fp.syswrite "START" } + end +end +EOF + unicorn -D -c $unicorn_config pid.ru + worker_wait_start + orig_master_pid=$unicorn_pid +} + +t_begin "read original worker pid" && { + orig_worker_pid=$(curl -sSf http://$listen/) + test -n "$orig_worker_pid" && kill -0 $orig_worker_pid +} + +t_begin "upgrade to new master" && { + kill -USR2 $orig_master_pid +} + +t_begin "kill old worker" && { + kill -WINCH $orig_master_pid +} + +t_begin "wait for new worker to start" && { + worker_wait_start + test $unicorn_pid -ne $orig_master_pid + new_master_pid=$unicorn_pid +} + +t_begin "old master pid is stashed in $pid.oldbin" && { + test -s "$pid.oldbin" + test $orig_master_pid -eq $(cat $pid.oldbin) +} + +t_begin "ensure old worker is no longer running" && { + i=0 + while kill -0 $orig_worker_pid 2>/dev/null + do + i=$(( $i + 1 )) + test $i -lt 600 || die "timed out" + sleep 1 + done +} + +t_begin "capture pid of new worker" && { + new_worker_pid=$(curl -sSf http://$listen/) +} + +t_begin "reload old master process" && { + kill -HUP $orig_master_pid + worker_wait_start +} + +t_begin "gracefully kill new master and ensure it dies" && { + kill -QUIT $new_master_pid + i=0 + while kill -0 $new_worker_pid 2>/dev/null + do + i=$(( $i + 1 )) + test $i -lt 600 || die "timed out" + sleep 1 + done +} + +t_begin "ensure $pid.oldbin does not exist" && { + i=0 + while test -s $pid.oldbin + do + i=$(( $i + 1 )) + test $i -lt 600 || die "timed out" + sleep 1 + done + while ! test -s $pid + do + i=$(( $i + 1 )) + test $i -lt 600 || die "timed out" + sleep 1 + done +} + +t_begin "ensure $pid is correct" && { + cur_master_pid=$(cat $pid) + test $orig_master_pid -eq $cur_master_pid +} + +t_begin "killing succeeds" && { + kill $orig_master_pid +} + +dbgcat r_err + +t_done |