Skip to content
This repository

3.1: Preventing assets from being cached in Rails.cache/Rack::Cache #2468

Closed
kamui opened this Issue August 08, 2011 · 13 comments

11 participants

Jack Chu Jon Leighton Michael Irwin Pratik Khadloya James Le Cuirot ehudros joe1chen Adam Hawkins Paul Meserve Steve Klabnik Thomas Klemm
Jack Chu

I have a Rails 3.1 app on Heroku that uses Cloudflare as a CDN. Cloudflare is a DNS based CDN, so you use their DNS servers for your domain. When a static asset is first requested, Cloudflare requests it from your web app, caches the file in it's CDN and then subsequent requests get served from the CDN. It's similar to how Amazon Cloudfront works, except that your asset host remains your app's host instead of some other domain name.

The issue arrises when you use:

config.action_controller.perform_caching = true

In 3.1, Rack::Cache seems to automatically use your config.cache_store to store assets/html fragments. Since Cloudflare is handling my asset caching, I want to stop Rack::Cache from caching assets to Rails.cache. In my case, I'm using redis-store, so my RedisToGo instance is being filled up quickly with static assets from Rack::Cache. This is kind of redundant since my CDN is going to be serving a majority of these requests. The few asset requests that Rails should get should be from Cloudflare, so it's fine if Rails serves this directly from the precompiled assets instead of from the Rails.cache.

I've considered disabling the Rack::Cache middleware, but then I lose out on page/action/fragment controller caching. I've tried setting my config.static_cache_control to private or no-cache, but no-cache would also prevent user browers from caching the assets and both still store assets in the cache.

I'm at a loss here. For now I've increased my RedisToGo instance, but that's not a good long term strategy.

Jack Chu

After some research and experimentation, I've ended up doing this in my config/application.rb for now:

if !Rails.env.development? && !Rails.env.test?
  config.middleware.insert_before Rack::Cache, Rack::Static, urls: [config.assets.prefix], root: 'public'
end

I've have a more detailed explanation in a blog post and also StackOverflow.

Michael Irwin
mdi commented October 06, 2011

I'm having/seeing the same issue. I would say this is a huge problem.

Pratik Khadloya

How do i check the list of assets cached in the Rails cache?

James Le Cuirot

The proposed workaround seems to conflict with Dragonfly. Plus not only are these assets being needlessly cached but it seems to prevent the assets from even being found at all in some instances. I haven't worked out exactly what conditions are causing this but I am seeing "The page you were looking for doesn't exist" for assets that are blatantly present under public/assets.

ehudros

+1 for fixing this. It's extremely strange (and quite unnecessary) to store static assets in the application cache by default.

joe1chen

Rack Cache should be configured differently for entity storage vs meta storage. Rack::Cache has two different storage areas: meta and entity stores. The metastore keeps high level information about each cache entry including HTTP request and response headers. This area stores small chunks of data that is accessed at a high frequency. The entitystore caches the response body content which can be a relatively large amount of data though it is accessed less frequently than the metastore

The below configuration caches the metastore info in memcached but the actual body of the assets to the file system.

Using memcached gem:

  config.action_dispatch.rack_cache = {
    :metastore    => 'memcached://localhost:11211/meta',
    :entitystore  => 'file:tmp/cache/rack/body',
    :allow_reload => false
  }

Using dalli gem

config.action_dispatch.rack_cache = {
  :metastore    => Dalli::Client.new,
  :entitystore  => 'file:tmp/cache/rack/body',
  :allow_reload => false
}

By the way this configuration is the recommendation for Heroku:
https://devcenter.heroku.com/articles/rack-cache-memcached-static-assets-rails31

Adam Hawkins

@kamui

I've considered disabling the Rack::Cache middleware, but then I lose out on page/action/fragment controller caching

Rack::Cache has nothing to do with action or fragment caching.

Jack Chu
kamui commented June 28, 2012

@twinturbo I meant that indirectly.

config.action_controller.perform_caching = true

This enables action/page/fragment caching and also turns on Rack::Cache in production. At the time, I didn't see another way of disabling Rack::Cache from storing static assets.

It looks like you can also disable Rack::Cache this way:

config.action_dispatch.rack_cache =  nil

but I didn't want to disable Rack::Cache entirely, I just wanted to disable it from storing static assets. But @joe1chen's suggestion seems like a good solution.

Paul Meserve
themgt commented July 05, 2012

@kamui +1 for fixing this - I have been seeing Rails make copies of compiled assets in its cache folder in production, and at time these actually get corrupted somehow, so the wrong files get served - I'm guessing some sort of threadsafe issue, but I don't see any benefit in trying to track it down since making a file cache of files is just wasting resources. Your fix to force them to be served with Rack::Static appears to work perfectly

An out of the box rails app in production is taking JS/CSS/image files and copying them into a file-based "cache". It seems like this should be disabled

Jon Leighton

I don't consider this a bug in Rails, for several reasons:

  • You could consider your Redis instance as an LRU cache, therefore the assets would get evicted once there is memory pressure.
  • It would only deal with Rack::Cache. Users might still need to configure other HTTP caches separately. Or maybe not: for example nginx by default evicts unused resources after 10 minutes.
  • We can't solve this by headers; that would cause the CDN to serve the assets with the wrong headers too.

This situation could be improved by documentation. It would be good to have something in the asset pipeline guide along the lines of "if your assets are being served by a CDN, ensure they don't stick around in your HTTP cache forever (by LRU eviction, configuration, etc)".

Steve Klabnik
Collaborator

I've written some documentation and put it in docrails here: rails/docrails@e85cb2d

Jon Leighton
Owner

Nice one Steve. Think we can close this then.

Jon Leighton jonleighton closed this November 09, 2012
Thomas Klemm

Thanks for the note!

Toshinori Kajihara kennyj referenced this issue from a commit in kennyj/rails November 09, 2012
Steve Klabnik Add note about asset pipeline and CDNs.
Fixes #2468.
e85cb2d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.