JSON-P Rack Handler 1

Posted by acts_as_flinn Mon, 16 Jun 2008 21:58:00 GMT

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.

JSON-P on Rails with JQuery

Posted by acts_as_flinn Sun, 20 Apr 2008 13:22:00 GMT

JSON-P based Comment

Over the last several months I’ve had the pleasure of developer JSON based commenting solutions for publishers. I couldn’t be happier using JSON as a transfer method for moving ActiveRecord based object from the database almost directly to the consumer. I’ve run into some difficulties here and there mainly due to the Same Origin Policy which is a bastard of a browser rule that makes sending JSON across domains difficult. Even though prototype.js seems to support sending requests across domains there is no native script transport in prototype and worse yet it appear some browsers won’t allow AJAX across subdomains to overcome the SOP rules. I searched all over the web and found a few hacks for adding the script tag to prototype.js but I found the best solution was built native into JQuery’s getJSON. I was really taken with JQuery’s ability to handle the script transport method out of the box but I think even more I’m taken with JQuery’s syntax. I had a conversation with Less Everything’s Steven Bristol about 6 months ago and he asked if I had checked out JQuery. I had briefly looked at it but I wasn’t wowed by it (probably because I didn’t use it on a project). Steven: “isn’t it the best thing you’ve ever seen?” Me: It’s ok. (*my answer to everything – ask my wife). Six months later I get to use it on a project and my answer to you Steven is YES it is the greatest.

JSON-P

Back to the subject… So the problem is that a browser doesn’t want to send a request across domains for fear that your secret info will be compromised by some would be hacker. The solution JSON padded with a callback method combined with a plain old script tag aka JSON-P. With JQuery the idea is that you can pass a URL into JQuery’s getJSON method with a ? for JQuery to bind it’s on changing function name that handles a callback. Sound like a hack? Yes but everyone is doing it. I’ve seen some skeptics but overall this is an accepted solution. And until JSONRequest is accepted as a safe cross site transport JSON-P is here to stay.

Does Rails support JSON-P?

Bet your ass it does. Right out of the box Rails supports the callback option to be passed in on a :json render. Like so:

render :json => @comments, :callback => params[:callback]

JSON-P Problems on Rails

So the issue you’ll run into primarily with JQuery is the changing name of the callback. JQuery binds a random function name to the request (presumably to protect from attacks of some sort, maybe for anti caching). The issue is that with action caching caches the entire response body meaning the next call has a stale cache (because of the now changed callback name) or has to generate a new response for a new callback name. While JSON is typically very light weight the to_json method can be expensive and when you’re dealing with high traffic situations you always want to squeeze every bit of performance out that you can. We also do paginated result sets with will_paginate so it means there is a bit of post processing after a plain old find_all_by_blah… The solution I’ve been using is a mix of cache_fu for model level caching along with fragment caching the to_json results then interpolating the cached json into the callback. Sound like a hack? It is but it feels a bit better when you crunch out over 500/reqs/s as opposed to 14/reqs/s.

For the future

Let’s cross our fingers for CouchDB. I’ve been experimenting with Couch over the last week or two and I’m really pleased with the possibility of a RESTful document based database with native JSON and coming support for MapReduce. The future looks bright, I can imagine not so distant support for native ruby object level persistence. Yay!

AJAX Tabs (Rails redux)

Posted by acts_as_flinn Sun, 25 Feb 2007 04:12:00 GMT

A while back I wrote a tutorial on AJAX Tabs as a plug for a PHP clone of Ruby on Rails called Biscuit. Well Biscuit is long gone, but the I still use the tutorial code, but just in Rails. So I came up with an update using Rails. Check out the Ruby on Rails AJAX Tabs Tutorial.