Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails Asset Caching Breaks First few page loads

We're using Rails asset caching for JS and CSS like this:

<%= stylesheet_link_tag 'reset','global','admins','autocomplete', 'date_input', 'tablesorter', 'partners', 'jqmodal', :media => 'screen', :cache => set_asset_cache(:admins) %>
<%= javascript_include_tag :defaults, 'autocomplete', 'searchbox', 'jqmodal', :cache => set_asset_cache(:admins) %>

In our deploy we call rake tmp:assets:clear each time. The problem is that the first few page loads after a deploy come up with no css or js on the page. I guess until the cached all.js and all.css have been regenerated.

We deploy many times per day and this is scary for any users who happen to come across a busted page.

Have people found any way to make this smoother so the new cached assets are guaranteed to be there on the first new page load?

like image 958
Brian Armstrong Avatar asked May 04 '10 23:05

Brian Armstrong


2 Answers

The AssetHat gem addresses this exact problem. Instead of concatenating assets the first time a page is loaded (which increases that page's load time), it concatenates assets on deploy instead. As a bonus, the gem also minifies your CSS and JS, which saves precious bytes.

After setup, usage is pretty simple:

  • Use include_css :bundle => 'admins' and include_js :bundle => 'admins' in your layout. (The bundle contents are set in a config file to keep your layout lightweight.)
  • Add rake asset_hat:minify to your deploy script. My company has been using it in production with Capistrano for about a year now.

There's more info in the readme and docs, and I'd be happy to hear any questions/ideas!

like image 119
Ron DeVera Avatar answered Oct 27 '22 01:10

Ron DeVera


You could try warming the cache during deployment using wget, as an example (shamelessly reposted):

wget -r -nd --delete-after http://whatever.com/~popular/page/

However, this would have to be executed after you switch your symlink to your new deployment. A possibly more elegant solution might be to call the asset caching methods manually in your deploy, though I'm not sure how feasible that is. Here's where the caching is performed in Rails:

# File vendor/rails/actionpack/lib/action_view/helpers/asset_tag_helper.rb, line       273
273:       def javascript_include_tag(*sources)
274:         options = sources.extract_options!.stringify_keys
275:         concat  = options.delete("concat")
276:         cache   = concat || options.delete("cache")
277:         recursive = options.delete("recursive")
278: 
279:         if concat || (ActionController::Base.perform_caching && cache)
280:           joined_javascript_name = (cache == true ? "all" : cache) + ".js"
281:           joined_javascript_path = File.join(joined_javascript_name[/^#{File::SEPARATOR}/] ? ASSETS_DIR : JAVASCRIPTS_DIR, joined_javascript_name)
282: 
283:           unless ActionController::Base.perform_caching && File.exists?(joined_javascript_path)
284:             write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources, recursive))
285:           end
286:           javascript_src_tag(joined_javascript_name, options)
287:         else
288:           expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n")
289:         end
290:       end

You might be able to modify the caching code and run it manually on deploy.

like image 25
robotmay Avatar answered Oct 27 '22 02:10

robotmay