Deploying Without Losing Style Points

You've done everything Steve Souders said to speed up your website. Perhaps you practice markup haiku, but you definitely combine stylesheets and scripts into single assets. You've made capistrano perform rolling restarts via haproxy so that your visitors always have mongrels waiting for you. Yet for some reason visitors receive an unstyled version of your site every time you deploy!

GetSatisfaction.com without its style
rails based Get Satisfaction missing its skin

You cannot serve what doesn't exist

For simplicity, assume we combine stylesheets using stylesheet_link_tag :all, :cache => true.

As capistrano finishing deploying current is re-symlinked to the new deploy. Before our mongrels finish restarting a request for all.css arrives. Since no mongrels running in the new current has rendered a template with our stylesheet_link_tag, the combined all.css won't exist. Your webserver returns a 404 and your visitor gets to see your dirty non-semantic html.

The file will not exist until a mongrel that has been restarted renders the pages. The time required to complete a rolling deploy and volume of requests can affect how long this takes. I've seen up to 30 seconds before enough mongrels have been restarted to generate the static content.

Doing it by hand

You need generate your combined assets progmatically on each deploy. For cliKball, we have a rake task that loads enough of an environment to run stylesheet_link_tag, which in turn generates our static files.

task :build_cache => :environment do
  include ActionView::Helpers::TagHelper
  include ActionView::Helpers::UrlHelper
  include ActionView::Helpers::AssetTagHelper
  stylesheet_link_tag :all, :cache => true
end

Then we added a hook to deploy:update_code to capistrano, so that our rake task is run before the symlink is modified.

after "deploy:update_code", "deploy:prepare_static_cache"

namespace :deploy do
  task :prepare_static_cache do
    # SASS -> CSS -> all.css; all.js
    run "cd #{release_path}; rake RAILS_ENV=#{rails_env} build_cache"
  end
end

But I use Passenger?

This was a very simple example but the issues and solutions are similar for more complicated environments. With Passenger, if you have a large number of processes for your site, your visitors could get a 404 while waiting for the processes to be reloaded. On userscripts.org it takes about 20 seconds to restart, leaving an undefined window, during which time 100 requests queue up. Knowing how visitors will be affected during these times is required if you want to always be shipping.

Since you are using 10 year expiry times on timestamp based urls, visitors might have a painful experience until the next time you deploy. So if you combine, you must take precautions when you deploy.


Share/Save/Bookmark

Published

Fri, 20 Feb 2009

View Comments


Want more like this?

Subscribe via RSS
or by email:

New Relic