diff options
-rw-r--r-- | SIGNALS | 6 | ||||
-rw-r--r-- | lib/rainbows/event_machine.rb | 10 | ||||
-rw-r--r-- | t/app_deferred.ru | 8 | ||||
-rwxr-xr-x | t/t0700-app-deferred.sh | 18 |
4 files changed, 34 insertions, 8 deletions
@@ -24,9 +24,9 @@ between \Rainbows!, unicorn and nginx. * INT/TERM - quick shutdown, kills all workers immediately * QUIT - graceful shutdown, waits for workers to finish their - current request before finishing. This currently does not - wait for requests deferred to a separate thread when using - EventMachine (when app.deferred?(env) => true) + current request before finishing. Since Rainbows 5.1.0 (Jan 2017), + this waits requests deferred to a separate thread with + EventMachine (app.deferred?(env) => true). * USR1 - reopen all logs owned by the master and all workers See Unicorn::Util.reopen_logs for what is considered a log. diff --git a/lib/rainbows/event_machine.rb b/lib/rainbows/event_machine.rb index b326e26..896fdac 100644 --- a/lib/rainbows/event_machine.rb +++ b/lib/rainbows/event_machine.rb @@ -65,6 +65,11 @@ module Rainbows::EventMachine end end + def defers_finished? + # EventMachine 1.0.0+ has defers_finished? + EM.respond_to?(:defers_finished?) ? EM.defers_finished? : true + 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) @@ -101,7 +106,10 @@ module Rainbows::EventMachine end end EM.add_periodic_timer(1) do - EM.stop if ! Rainbows.tick && conns.empty? && EM.reactor_running? + if ! Rainbows.tick && conns.empty? && defers_finished? && + EM.reactor_running? + EM.stop + end end LISTENERS.map! do |s| EM.watch(s, Rainbows::EventMachine::Server) do |c| diff --git a/t/app_deferred.ru b/t/app_deferred.ru index a70b33b..b3d7ff1 100644 --- a/t/app_deferred.ru +++ b/t/app_deferred.ru @@ -6,12 +6,18 @@ class DeferredApp < Struct.new(:app) def deferred?(env) - env["PATH_INFO"] == "/deferred" + env["PATH_INFO"] =~ %r{\A/deferred} end def call(env) env["rack.multithread"] or raise RuntimeError, "rack.multithread not true" body = "#{Thread.current.inspect}\n" + if env["PATH_INFO"] =~ %r{\A/deferred(\d+)} + delay = $1.to_i + File.open(ENV['fifo'], 'w') { |fp| fp.write "sleeping #{delay}s\n" } + body = "deferred sleep\n" + sleep(delay) + end headers = { "Content-Type" => "text/plain", "Content-Length" => body.size.to_s, diff --git a/t/t0700-app-deferred.sh b/t/t0700-app-deferred.sh index 90614b2..188fdde 100755 --- a/t/t0700-app-deferred.sh +++ b/t/t0700-app-deferred.sh @@ -15,7 +15,7 @@ CONFIG_RU=app_deferred.ru t_begin "setup and start" && { rainbows_setup rtmpfiles deferred_err deferred_out sync_err sync_out - rainbows -D -c $unicorn_config $CONFIG_RU + fifo=$fifo rainbows -D -c $unicorn_config $CONFIG_RU rainbows_wait_start } @@ -36,8 +36,20 @@ t_begin "deferred requests run in a different thread" && { test x"$(uniq < $deferred_out)" != x"$sync_thread" } -t_begin "termination signal sent" && { - kill $rainbows_pid +t_begin "deferred requests run after graceful shutdown" && { + # XXX sleeping 5s ought to be enough for SIGQUIT to arrive, + # hard to tell with overloaded systems... + s=5 + curl -sSf --no-buffer http://$listen/deferred$s \ + >$deferred_out 2>$deferred_err & + curl_pid=$! + msg="$(cat $fifo)" + kill -QUIT $rainbows_pid + test x"$msg" = x"sleeping ${s}s" + wait $curl_pid # for curl to finish + test $? -eq 0 + test ! -s $deferred_err + test x"$(cat $deferred_out)" = 'xdeferred sleep' } t_begin "no errors in stderr" && check_stderr |