From 57909fb76dd0bcf95bed94ec41933ec85afda885 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 4 May 2010 13:23:38 -0700 Subject: eventmachine: add app.deferred?(env) support as middleware Merb (and possibly other) frameworks that support conditionally deferred app dispatch can now use it just like Ebb and Thin. http://brainspl.at/articles/2008/04/18/deferred-requests-with-merb-ebb-and-thin --- lib/rainbows/event_machine.rb | 23 ++++++++++++++++++++++ t/app_deferred.ru | 22 +++++++++++++++++++++ t/t0700-app-deferred.sh | 45 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 t/app_deferred.ru create mode 100755 t/t0700-app-deferred.sh diff --git a/lib/rainbows/event_machine.rb b/lib/rainbows/event_machine.rb index 3dd329a..4b852c9 100644 --- a/lib/rainbows/event_machine.rb +++ b/lib/rainbows/event_machine.rb @@ -34,6 +34,11 @@ module Rainbows # which allows each request to run inside its own \Fiber after # all request processing is complete. # + # Merb (and other frameworks/apps) supporting +deferred?+ execution as + # documented at http://brainspl.at/articles/2008/04/18/deferred-requests-with-merb-ebb-and-thin + # will also get the ability to conditionally defer request processing + # to a separate thread. + # # This model does not implement as streaming "rack.input" which allows # the Rack application to process data as it arrives. This means # "rack.input" will be fully buffered in memory or to a temporary file @@ -200,11 +205,29 @@ module Rainbows end end + # Middleware that will run the app dispatch in a separate thread. + # This middleware is automatically loaded by Rainbows! when using + # EventMachine and if the app responds to the +deferred?+ method. + class TryDefer < Struct.new(:app) + def call(env) + if app.deferred?(env) + EM.defer(proc { catch(:async) { app.call(env) } }, + env[EvCore::ASYNC_CALLBACK]) + # all of the async/deferred stuff breaks Rack::Lint :< + nil + else + app.call(env) + end + 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) def worker_loop(worker) init_worker_process(worker) + G.server.app.respond_to?(:deferred?) and + G.server.app = TryDefer[G.server.app] # enable them both, should be non-fatal if not supported EM.epoll diff --git a/t/app_deferred.ru b/t/app_deferred.ru new file mode 100644 index 0000000..179ac95 --- /dev/null +++ b/t/app_deferred.ru @@ -0,0 +1,22 @@ +#\-E none +# can't use non-compatible middleware that doesn't pass "deferered?" calls +# +# used for testing deferred actions for Merb and possibly other frameworks +# ref: http://brainspl.at/articles/2008/04/18/deferred-requests-with-merb-ebb-and-thin + +class DeferredApp < Struct.new(:app) + def deferred?(env) + env["PATH_INFO"] == "/deferred" + end + + def call(env) + body = "#{Thread.current.inspect}\n" + headers = { + "Content-Type" => "text/plain", + "Content-Length" => body.size.to_s, + } + [ 200, headers, [ body ] ] + end +end + +run DeferredApp.new diff --git a/t/t0700-app-deferred.sh b/t/t0700-app-deferred.sh new file mode 100755 index 0000000..8d4b4e2 --- /dev/null +++ b/t/t0700-app-deferred.sh @@ -0,0 +1,45 @@ +#!/bin/sh +. ./test-lib.sh +case $model in +EventMachine) ;; +*) + t_info "skipping $T since it's not compatible with $model" + exit 0 + ;; +esac + +t_plan 5 "basic test for app.deferred? usage" + +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 + rainbows_wait_start +} + +t_begin "synchronous requests run in the same thread" && { + curl --no-buffer -sSf http://$listen/ >> $sync_out 2>> $sync_err & + curl --no-buffer -sSf http://$listen/ >> $sync_out 2>> $sync_err & + curl --no-buffer -sSf http://$listen/ >> $sync_out 2>> $sync_err & + wait + test ! -s $sync_err + test 3 -eq "$(wc -l < $sync_out)" + test 1 -eq "$(uniq < $sync_out | wc -l)" +} + +t_begin "deferred requests run in a different thread" && { + curl -sSf http://$listen/deferred >> $deferred_out 2>> $deferred_err + test ! -s $deferred_err + sync_thread="$(uniq < $sync_out)" + test x"$(uniq < $deferred_out)" != x"$sync_thread" +} + +t_begin "termination signal sent" && { + kill $rainbows_pid +} + +t_begin "no errors in stderr" && check_stderr + +t_done -- cgit v1.2.3-24-ge0c7