Skip to content

Instantly share code, notes, and snippets.

@woodie
Created March 14, 2010 08:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save woodie/331854 to your computer and use it in GitHub Desktop.
Save woodie/331854 to your computer and use it in GitHub Desktop.
Deferred Dispatcher

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.

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
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
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')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment