4 comments

[ 3.0 ms ] story [ 18.0 ms ] thread
Great summary.

One problem that tripped me up with http caching in rails was invalidating client http caches when a template changes and you're doing conditional etags based on an object's updated at timestamp. So basically when you do fresh_when(object), your rails app basically will only tell an http client this thing is stale if the object's updated_at timestamp changes. It doesn't take into account if the actually view code changes. Even worse, when your assets get recompiled (app-123.css is now app-234.css) and the old ones don't exist anymore, your app breaks.

I wrote about a way to fix that with a little gem I made:

http://ninjasandrobots.com/rails-caching-a-problem-with-etag...

Nice fix!

Declared ETags, together with Russian Doll caching, take the next step: automatically mixing your template and asset versions into your ETags.

To include the template's version in the ETag:

  # Incorporate the cache version for this action into our ETag.
  # This allows template changes to bubble up into HTTP cache
  # freshness and bust browser caches when we make changes.
  etag do
    begin
      CacheDigests::TemplateDigestor.digest(
        "#{controller_name}/#{action_name}",
        request.format.try(:to_sym), lookup_context)
    rescue ActionView::MissingTemplate => e
      '' # Ignore missing templates
    end
  end
Similarly, for your JavaScript and CSS assets:

  ASSET_FRESHNESS_PATHS = %w( application.js application.css )

  # HTML pages cached by the browser should be updated whenever we change
  # our assets, so we include application.css and application.js digests
  # in ETags for HTML pages.
  etag do
    if request.format.try(:html?)
      ASSET_FRESHNESS_PATHS.map { |p| asset_digest_for_etag(p) }.join('-')
    end
  end

  # Check precompiled asset manifest (production) or compute the digest (dev).
  def asset_digest_for_etag(logical_path)
    if manifest = Rails.application.config.assets.digests
      manifest[logical_path]
    else
      Rails.application.assets[logical_path].digest
    end
  end
This avoids the need to use global versions that blow all browser caches. Neatly contains the scope of "freshness fallout" when you tweak a view and deploy.
This is an old(ish) article. Any particular reason it is showing up here now ?