JSON-P Rack Handler 1
Updated 2008-06-19 – better support Halcyon
JSON-P Rack Handler
Juicing Ruby
I’ve been trying to find ways to squeeze all the juice out of Ruby lately. So many blog posts talk about how Rails doesn’t scale, Ruby is slow, blah blah. I had a convo with another developer at work today that went something like this:
Me: if we’re willing to do anything for performance we’d we just switch to Java
Jared: Yeah let’s not do that.
Switching to Java would be a big trade off in performance but also in development time. That’s a big trade off that none of us around here think is worth. That’s where this Rack handler comes in.
JSON-P Caching
I’ve posted before about how to use JSON-P in Rails and how to cache JSON-P in Rails with fairly decent results (500+ reqs/sec) but I felt like I could do better. Action caching always seemed like the best way to cache the full JSON output of a request but the fact that jQuery uses a dynamic callback takes action caching out of the equation.
Speeding up JSON-P
500+ reqs/sec is good and all but I felt like if I could action cache then somehow pad that cached JSON result with the callback I’d get better performance. I thought I’d see what Merb could offer on the JSON-P front. In my quest to juice Ruby my test setup looks like this: Merb, Datamapper, Memcached (the c gem), Memcached (the server) and Ebb. I’m really impressed with Merb and Datamapper in terms of development and I’m equally impressed with Rack and Ebb for performance.
I read recently it’s possible to use Rack to filter results to gzip output, which got me thinking. Why not try to do the same to pad my action cached JSON. Well it is possible and I’m squeezing out JSON-P at ~1200 reqs/sec with the JSON-P Rack Handler with my test stack.
JSON-P Rack Handler
# config/jsonp.rb
class JsonP
def initialize(app)
@app = app
end
def call(env)
status, headers, response = @app.call(env)
request = Rack::Request.new(env)
response = pad(request.params.delete('callback'), response) if request.params.include?('callback')
[status, headers, response]
end
def pad(callback, response, body = "")
response.each{ |s| body << s }
"#{callback}(#{body})"
end
end
Config
# config/rack.rb
require 'config/jsonp'
use JsonP
run Merb::Rack::Application.new
In Halcyon
# runner.ru
...
require 'config/jsonp.halcyon'
use JsonP
run Halcyon::Runner.new
Rails Support Soon
One last thing… whenever Rails starts using Rack, you’ll be able to use this in your Rails app.
About Me: I’m a developer with Sports Technologies, a Rails firm specializing in community focused web applications aimed at sports and entertainment. We’re always looking for Rails talent, if you’re looking to work on rewarding high profile projects with a seasoned team of professionals give us a shout.
Trackbacks
Use the following link to trackback from your own site:
http://www.actsasflinn.com/trackbacks?article_id=json-p-rack-handler&day=16&month=06&year=2008



runner.rufile to:use Rack::JSONPand it will behave exactly as above. Thanks for the excellent contribution. Hopefully it will make it into the Rack codebase and I can remove it from Halcyon, but for now, that's where it will live. Matt