Asynchronous session content injection
George Malamidis, August 6th, 2009Applying a clear distinction between stateless and stateful content when designing a web application is tricky but worth tackling early so that content not specific to user sessions can benefit from web caching. The technique we are trying out for scramble.com reminds me of what I described in State separation and was introduced to me by Mike Jones who was inspired by the Dynamically Update Cached Pages chapter in Advanced Rails Recipes.
The idea involves serving non session specific resources independent from personalized content and use AJAX calls to inject the page with session specific content.
require 'rubygems'
require 'sinatra'
require 'json'
configure do
enable :sessions
end
get '/' do
headers['Cache-Control'] = 'max-age=60, must-revalidate'
erb :index
end
get '/userinfo' do
if session[:user]
JSON.dump(:user => session[:user])
else
halt 401
end
end
get '/login' do
session[:user] = 'rock'
redirect '/'
end
get '/logout' do
session.clear
redirect '/'
end
Notice some of the headers for '/':
$ curl -I http://localhost:4567/ Cache-Control: max-age=60, must-revalidate Set-Cookie: rack.session=BAh7AA%3D%3D%0A; path=/
The Cache-Control policy instructs a web cache to keep this version of the resource for 60 seconds before requesting a fresh one. Set-Cookie however will usually cause a web cache to never store the response and always query its back end.
The following configuration tells Varnish to throw away the cookie from any request/response that doesn’ match one of the URLs that require authorization, thus causing it to react to response cache policies.
sub vcl_recv {
if (req.url !~ "^(/login|/logout|/userinfo)") {
unset req.http.cookie;
}
}
sub vcl_fetch {
if (req.url !~ "^(/login|/logout|/userinfo)") {
unset obj.http.set-cookie;
}
}
A snippet from the HTML response for '/':
<h1>Hi</h1> <div id="nav"> <a href="/login" class='login-control'>Login</a> </div>
… and the javascript for asynchronously injecting session data to the page:
$(function() {
$.getJSON('/userinfo', function(data) {
$('h1').text('Hi ' + data.user);
$('#nav .login-control').attr('href', '/logout').html('logout');
})
})
In summary, it is likely that a website will have significant amounts of content that is intended for everyone without the need for personalization. The performance of serving that content can benefit from web caching, but that becomes difficult as many websites’ user experience depends on the presence of user sessions. Separating stateless from session specific content at the resource level and using a combination of HTTP and AJAX to merge the results of requests for both types of resources will make stateless content cacheable by decoupling it from the unnecessary cookie dependency.
Runnable code example : http://pastie.org/573878


August 12th, 2009 at 3:15 pm
[...] nutrun » Blog Archive » Asynchronous session content injection [...]
August 17th, 2009 at 2:22 pm
Very nice post. We do this to some extent on http://www.fannation.com/ and http://teamusa.org/. You get much better mileage out of action cache than assembling user specific cached fragments into a response. In some places we pass some user specific info via cookie. I’ve found most Rails developers forget you can use cookies and javascript to deliver user customization in favor of writing server side code. I tend to prefer passing the computation onto the client.