public
Last active

Deferred Dispatcher

  • Download Gist
README.markdown
Markdown

The DeferredDispatcher is a way to get around the 30-second limit for App Engine requests. We don't want spin-up to be a "show stopper", and we want every request to have a full 30 seconds to do "real work". We added this to appengine-rack 0.0.7, so folks won't need an additional require. The DeferredDispatcher works by using redirects to break up a loading request into 3 parts.

  1. initialize the jruby runtime and jruby-rack
  2. require the framework, and create a new dispatcher
  3. process the real request, with a full 30 seconds

We are inside jruby-rack, so obviously the runtime and Jruby-rack have initialized, so first we set @runtime to true (from nil) and redirect. Next pass, we notice that the @rack_app is nil, so we require the framework (which can take some time) and create a new dispatcher (these have been passed in as a hash) and redirect. In the final pass, we hand the environment to the dispatcher, to do the original "work" of the request.

appengine-rack lib appengine-rack.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
module AppEngine
module Rack
 
# ...
 
class DeferredDispatcher
def initialize args
@args = args
end
def call env
if @runtime.nil?
@runtime = true
# 1: redirect with runtime and jruby-rack loaded
redirect_or_error(env)
elsif @rack_app.nil?
require @args[:require]
@rack_app = Object.module_eval(@args[:dispatch]).new
# 2: redirect with framework required & dispatched
redirect_or_error(env)
else
# 3: process all other requests
@rack_app.call(env)
end
end
def redirect_or_error(env)
if env['REQUEST_METHOD'].eql?('GET')
redir_url = env['REQUEST_URI'] +
(env['QUERY_STRING'].eql?('') ? '?' : '&') +
"_spinup_=#{Time.now.to_i}"
res = ::Rack::Response.new('*', 302)
res['Location'] = redir_url
res.finish
else
::Rack::Response.new('Service Unavailable', 503).finish
end
end
end
 
# ...
 
end
end
complex config.ru
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
require 'appengine-rack'
AppEngine::Rack.configure_app(
:application => 'complex-app',
:precompilation_enabled => true,
:sessions_enabled => true,
:version => "1")
 
AppEngine::Rack.app.resource_files.exclude :rails_excludes
ENV['RAILS_ENV'] = AppEngine::Rack.environment
 
deferred_dispatcher = AppEngine::Rack::DeferredDispatcher.new(
:require => File.expand_path('../config/environment', __FILE__),
:dispatch => 'ActionController::Dispatcher')
map '/admin' do
use AppEngine::Rack::AdminRequired
run deferred_dispatcher
end
map '/' do
use AppEngine::Rack::LoginRequired
run deferred_dispatcher
end
simple config.ru
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13
require 'appengine-rack'
AppEngine::Rack.configure_app(
:application => 'simple-app',
:precompilation_enabled => true,
:sessions_enabled => true,
:version => "1")
 
AppEngine::Rack.app.resource_files.exclude :rails_excludes
ENV['RAILS_ENV'] = AppEngine::Rack.environment
 
run AppEngine::Rack::DeferredDispatcher.new(
:require => File.expand_path('../config/environment', __FILE__),
:dispatch => 'ActionController::Dispatcher')

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.