about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2009-10-30 18:14:37 -0700
committerEric Wong <normalperson@yhbt.net>2009-10-30 18:14:37 -0700
commitb96822f15c9ede2a0053afeb1a5f43d3df7d7d3d (patch)
tree15be0d666a48ebd0e82e9fd8e0245195d6d9faf9
parentf7189b5074ea99519a2a005c8b0f369bc1fd3a39 (diff)
downloadrainbows-b96822f15c9ede2a0053afeb1a5f43d3df7d7d3d.tar.gz
This module will be reused in upcoming Rev-derived concurrency
models.
-rw-r--r--lib/rainbows/rev.rb19
-rw-r--r--lib/rainbows/rev/heartbeat.rb27
-rw-r--r--t/heartbeat-timeout.ru13
-rwxr-xr-xt/t0004-heartbeat-timeout.sh62
4 files changed, 103 insertions, 18 deletions
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