From b96822f15c9ede2a0053afeb1a5f43d3df7d7d3d Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 30 Oct 2009 18:14:37 -0700 Subject: rev: split out heartbeat class This module will be reused in upcoming Rev-derived concurrency models. --- lib/rainbows/rev.rb | 19 +------------ lib/rainbows/rev/heartbeat.rb | 27 +++++++++++++++++++ t/heartbeat-timeout.ru | 13 +++++++++ t/t0004-heartbeat-timeout.sh | 62 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 18 deletions(-) create mode 100644 lib/rainbows/rev/heartbeat.rb create mode 100644 t/heartbeat-timeout.ru create mode 100755 t/t0004-heartbeat-timeout.sh diff --git a/lib/rainbows/rev.rb b/lib/rainbows/rev.rb index d2bbc49..a1a4e6a 100644 --- a/lib/rainbows/rev.rb +++ b/lib/rainbows/rev.rb @@ -1,6 +1,5 @@ # -*- encoding: binary -*- -require 'rev' -Rev::VERSION >= '0.3.0' or abort 'rev >= 0.3.0 is required' +require 'rainbows/rev/heartbeat' require 'rainbows/ev_core' module Rainbows @@ -168,22 +167,6 @@ module Rainbows end end - # This timer handles the fchmod heartbeat to prevent our master - # from killing us. - class Heartbeat < ::Rev::TimerWatcher - G = Rainbows::G - - def initialize(tmp) - @m, @tmp = 0, tmp - super(1, true) - end - - def on_timer - @tmp.chmod(@m = 0 == @m ? 1 : 0) - exit if (! G.alive && G.cur <= 0) - end - end - # runs inside each forked worker, this sits around and waits # for connections and doesn't die until the parent dies (or is # given a INT, QUIT, or TERM signal) diff --git a/lib/rainbows/rev/heartbeat.rb b/lib/rainbows/rev/heartbeat.rb new file mode 100644 index 0000000..755b136 --- /dev/null +++ b/lib/rainbows/rev/heartbeat.rb @@ -0,0 +1,27 @@ +# -*- encoding: binary -*- +require 'rev' +Rev::VERSION >= '0.3.0' or abort 'rev >= 0.3.0 is required' + +module Rainbows + module Rev + + # This class handles the Unicorn fchmod heartbeat mechanism + # in Rev-based concurrency models to prevent the master + # process from killing us unless we're blocked. This class + # will also detect and execute the graceful exit if triggered + # by SIGQUIT + class Heartbeat < ::Rev::TimerWatcher + # +tmp+ must be a +File+ that responds to +chmod+ + def initialize(tmp) + @m, @tmp = 0, tmp + super(1, true) + end + + def on_timer + @tmp.chmod(@m = 0 == @m ? 1 : 0) + exit if (! G.alive && G.cur <= 0) + end + + end + end +end diff --git a/t/heartbeat-timeout.ru b/t/heartbeat-timeout.ru new file mode 100644 index 0000000..fff0c40 --- /dev/null +++ b/t/heartbeat-timeout.ru @@ -0,0 +1,13 @@ +use Rack::ContentLength +fifo = ENV['FIFO_PATH'] or abort "FIFO_PATH not defined" +headers = { 'Content-Type' => 'text/plain' } +run lambda { |env| + case env['PATH_INFO'] + when "/block-forever" + # this should block forever (or until somebody opens it for reading) + File.open(fifo, "rb") { |fp| fp.syswrite("NEVER\n") } + [ 500, headers, [ "Should never get here\n" ] ] + else + [ 200, headers, [ "#$$\n" ] ] + end +} diff --git a/t/t0004-heartbeat-timeout.sh b/t/t0004-heartbeat-timeout.sh new file mode 100755 index 0000000..64ad207 --- /dev/null +++ b/t/t0004-heartbeat-timeout.sh @@ -0,0 +1,62 @@ +#!/bin/sh +. ./test-lib.sh + +t_plan 9 "heartbeat/timeout test for $model" + +t_begin "setup and startup" && { + rainbows_setup $model + echo timeout 3 >> $unicorn_config + echo preload_app true >> $unicorn_config + FIFO_PATH=$fifo rainbows -D heartbeat-timeout.ru -c $unicorn_config + rainbows_wait_start +} + +t_begin "read worker PID" && { + worker_pid=$(curl -sSf http://$listen/) + t_info "worker_pid=$worker_pid" +} + +t_begin "sleep for a bit, ensure worker PID does not change" && { + sleep 4 + test $(curl -sSf http://$listen/) -eq $worker_pid +} + +t_begin "block the worker process to force it to die" && { + t0=$(date +%s) + err="$(curl -sSf http://$listen/block-forever 2>&1 || :)" + t1=$(date +%s) + elapsed=$(($t1 - $t0)) + t_info "elapsed=$elapsed err=$err" + test x"$err" != x"Should never get here" + test x"$err" != x"$worker_pid" +} + +t_begin "ensure timeout took 2-6 seconds" && { + test $elapsed -ge 2 + test $elapsed -le 6 # give it some slack in case box is bogged down +} + +t_begin "wait for new worker to start up" && { + test x = x"$(cat $fifo)" +} + +t_begin "we get a fresh new worker process" && { + new_worker_pid=$(curl -sSf http://$listen/) + test $new_worker_pid -ne $worker_pid +} + +t_begin "SIGSTOP and SIGCONT on rainbows master does not kill worker" && { + kill -STOP $rainbows_pid + sleep 4 + kill -CONT $rainbows_pid + sleep 2 + test $new_worker_pid -eq $(curl -sSf http://$listen/) +} + +t_begin "stop server" && { + kill $rainbows_pid +} + +dbgcat r_err + +t_done -- cgit v1.2.3-24-ge0c7