Skip to content

Instantly share code, notes, and snippets.

@schneems
Created March 18, 2012 23:13
Show Gist options
  • Select an option

  • Save schneems/2084471 to your computer and use it in GitHub Desktop.

Select an option

Save schneems/2084471 to your computer and use it in GitHub Desktop.

Revisions

  1. schneems revised this gist Apr 3, 2012. 1 changed file with 94 additions and 99 deletions.
    193 changes: 94 additions & 99 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -1,198 +1,193 @@
    # Rack Cache on Cedar Rails 3.1+ Applications
    <p class="callout" markdown="1">
    Though slightly more complex, using a CDN is the most performant option for serving static assets. See the [CDN asset host](cdn-asset-host-rails31) article for more details.
    </p>

    Ruby on Rails applications should use [Rack::Cache](http://rtomayko.github.com/rack-cache/) to efficiently serve assets on the [Cedar stack](cedar). Proper Rack::Cache usage improves response time, decreases load and is important when serving static assets through your application.

    Cedar does not include a reverse proxy cache such as Varnish, preferring to empower developers to choose the CDN or caching solution that best serves their needs. You can use `Rack::Cache` your Rails application to improve response time and decrease load. This is important if you're using the asset pipeline and serving static assets through your application.
    This article will summarize the concepts of caching assets using Rack::Cache and walk you through the appropriate configuration of a Rails 3.1 application and the asset pipeline.

    <p class="note" markdown="1">
    Sample code for this article's [reference application](https://github.com/heroku/rack-cache-demo) is available on
    GitHub and can be seen running at
    [http://rack-cache-demo.herokuapp.com/](http://rack-cache-demo.herokuapp.com/)
    </p>

    Using Rack::Cache
    -----------------

    `Rack::Cache` is rack middleware that enables HTTP caching on your application and can allow us to serve assets from a storage backend without requiring work from your main Rails application. Many Ruby web frameworks including Rails 3+ and Sinatra, are built on top of Rack. At a high level, Rack works by taking a request and passing it through a series of steps, called middleware. Each Rack middleware performs some action, then passes the request to the next middleware in the rack. Eventually the request is properly formatted and it will reach your application.

    Rack is written to be lightweight and flexible. If middleware can respond to a request directly, then it doesn't have to hit your Rails application. This means that the request is retuned faster, and the overall load on the Rails application is decreased.
    ## Understanding Rack

    Rack::Cache is [Rack](http://rack.rubyforge.org/doc/) middleware that enables HTTP caching on your application and allows an application to serve assets from a storage backend without requiring work from the main Rails application. Many Ruby web frameworks, including Rails 3+ and Sinatra, are built on top of Rack.

    Rack::Cache Storage
    -------------------
    `Rack::Cache` has two different storage areas, meta and entity store, which can be configured for use with three different storage backends ,file, heap, and memcache. The metastore in `Rack::Cache` keeps high level information about each cache entry including headers. The entitystore is the response body content, the actual object being returned through the server. Storing data in a file is slow but memory efficient; using heap means your process' memory will be used which is quicker, but can have serious impacts; and using memcache is the fastest option, but isn't well suited to store large objects.
    At a high level, Rack works by taking a request and passing it through a series of steps, called middleware. Each Rack middleware performs some action, then passes the request to the next middleware in the rack. Eventually the request is properly formatted and it will reach your application.

    Rack is written to be lightweight and flexible. If middleware can respond to a request directly, then it doesn't have to hit your Rails application. This means that the request is retuned faster, and the overall load on the Rails application is decreased.

    Since the metastore is accessed frequently and is much smaller than the entitystore, you can use memcache for the backend, but to keep the amount of data stored in Memcache to a minimum, you can write the entitystore to a temporary file. Heap storage is not recommended. For more information on the entity and meta stores read more about [Rack Cache Storage](http://rtomayko.github.com/rack-cache/storage).

    ## Rack::Cache storage

    Configure Memcache
    ------------------
    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.

    <p class="callout" markdown="1">Configuring your application to use [Memcache](http://devcenter.heroku.com/articles/memcache)</p>
    Rack::Cache ships with three different storage engines: `file`, `heap`, and `memcache`. Storing data in the `file` engine is slower but memory efficient. Using `heap` means your process' memory will be used which is quicker but can have an impact on performance if it grows unbounded. Using `memcache` is the fastest option though it isn't well suited to store large objects.

    Since you will use memcache in your `Rack::Cache` metastore, you will first need to add it to your project. Heroku recommends using Memcache with the Dalli gem as part of your `Rack::Cache` backend.
    <div class="callout" markdown="1">
    For more information on the entity and meta stores read about [Rack Cache Storage](http://rtomayko.github.com/rack-cache/storage).
    </div>

    Using the metastore with the `memcache` storage engine, which allows very quick access to shared meta-data, while using the `file` engine for the entitystore and its larger objects results in an efficient and predictable application performance profile and is recommended on Heroku.

    You'll need to configure your Heroku application to use Memcache.
    ## Install Memcache locally

    :::term
    $ heroku addons:add memcache
    <div class="callout" markdown="1">
    Local installation instructions for other OSs can be found in the [Memcache add-on article](http://devcenter.heroku.com/articles/memcache).
    </div>

    To get this running on your local machine you will need to have Memcache installed. You can install it using a tool such as homebrew.
    To run your application locally and test the Rack::Cache setup you will need to have Memcache installed. You can install it on Mac OSX using a tool such as homebrew.

    :::term
    $ brew install memcache
    $ brew install memcached

    At the end of installation homebrew will give you instructions on how to start Memcache manually and automatically on system start.

    You can then tell your application to use Dalli, Heroku's preferred Memcache client. In your Gemfile add
    ## Configure Rails cache-store

    :::term
    Heroku recommends using Memcache with the [Dalli](https://github.com/mperham/dalli) gem as part of your `Rack::Cache` backend. In your `Gemfile` add:

    :::ruby
    gem 'dalli'

    Then in your `config/application.rb` add
    After running `bundle install` to establish Dalli as an application dependency tell Rails to use the Dalli client for its cache-store in `config/application.rb`.

    :::term
    :::ruby
    config.cache_store = :dalli_store

    If you start a Rails console session you should now have access to Memcache
    Confirm dalli/memcache configuration by starting a local Rails console session and getting/setting a simple key-value.

    :::term
    $ rails c
    > memcache = Dalli::Client.new
    > memcache.set('foo', 'bar')
    > memcache.get('foo') # => 'bar'
    > memcache.get('foo')
    'bar'

    Once you have [configured your application to use memcached](http://devcenter.heroku.com/articles/memcache), you can configure `Rack::Cache` for use in your application.
    Once you've configured your application to use memcached it's now time to configure Rack::Cache.

    ## Configure Rack::Cache

    Configure Rack::Cache on Rails 3.1+
    -----------------------------------------------------
    Modify your `config/environments/production.rb` environment file to specify the appropriate storage backends for Rails' built-in Rack::Cache integration.

    For Rails 3.1+ you will need to modify your `config/production.rb` to tell rack_cache to use Memcache. By default `Dalli::Client.new` will look for the proper environment variables (ENV["MEMCACHE_SERVERS"]) when deployed to Heroku, and otherwise will default to localhost and the default port. If you want, rather than specifying an object, you can pass the connection string needed to talk to an external memcache server.
    :::ruby
    config.action_dispatch.rack_cache = {
    :metastore => Dalli::Client.new,
    :entitystore => 'file:tmp/cache/rack/body',
    :allow_reload => false
    }

    <p class="note" markdown="1">
    If not specified, `Dalli::Client.new` automatically retrieves the Memcache server location from the `MEMCACHE_SERVERS` environment variable. Otherwise it will default to localhost and default port.
    </p>

    :::term
    # This will tell `Rack::Cache` to use the default settings of Dalli, Heroku's recommended Memcache Gem
    config.action_dispatch.rack_cache = {
    :metastore => Dalli::Client.new,
    :entitystore => 'file:tmp/cache/rack/body',
    :allow_reload => false }
    ## Serve static assets

    You can set the entitystore to write to a temporary file in the rails directory.
    <div class="callout" markdown="1">
    See the `production.rb` config of the [reference application](https://github.com/heroku/rack-cache-demo/blob/master/config/environments/production.rb) on GitHub.
    </div>

    To allow your application to serve static files from /public you need to set `config.servestatic_assets` to true.
    To allow your application to properly serve, invalidate and refresh static assets several config settings must be updated in `config/environments/production.rb`. Allow Rails to serve assets with the `serve_static_assets` setting.

    :::term
    # This will allow Action::Dispatch to serve files from /public when set to true
    :::ruby
    config.serve_static_assets = true

    Additionally, specify how long an item should stay cached by setting the Cache-Control headers. Without a Cache-Control header static files will not be stored by `Rack::Cache`

    Finally you need to tell the cache how long an item should stay in cache by setting the Cache-Control headers. Without a Cache-Control header, static files will not be stored by `Rack::Cache`

    :::term
    # This will set Cache-Control headers used by browsers
    :::ruby
    config.static_cache_control = "public, max-age=2592000"

    These settings tell `Rack::Cache` to store static elements for a very long time. To properly invalidate modified files Rails updates a hash digest in the file name. Enable this approach with the `config.assets.digest` setting.

    Since these settings will tell `Rack::Cache` to store static elements for a very long time, it is important that you let your cache store know when you change a file. Typically cache invalidation can be very tricky, so to avoid that problem you can ensure that Rails generates a new file name every time you modify a file. This is done by using a hash digest such as MD5 on your files, tell Rails to do this automatically by setting `config.assets.digest` to true.

    :::term
    # Generate digests for assets URLs
    :::ruby
    config.assets.digest = true

    You also want to ensure that caching is turned on.
    You also want to confirm that caching is turned on in production.

    # Enables caching including `Rack::Cache`
    :::ruby
    config.action_controller.perform_caching = true

    ## Provision Memcache add-on

    Since you will use memcache as your `Rack::Cache` metastore, you will need to add the [Memcache add-on](https://addons.heroku.com/memcache) to your application on Heroku.

    :::term
    $ heroku addons:add memcache
    ----> Adding memcache to myapp... done, v25 (free)

    ## Caching in production

    Deploy the application to Heroku and use the `heroku logs` command to view cache output.

    :::term
    $ git push heroku master
    $ heroku logs --ps web -t

    <div class="callout" markdown="1">
    Using a hard refresh clears your browser cache and is useful for forcing asset requests.
    </div>

    Once all of this is set up correctly, you should see cache lines in your production log. The 'miss, store' indicates that the item was not found in the cache but has been saved for the next request
    You should see `cache` entries in your production log-stream. Tailing `miss, store` tokens indicate that the item was not found in the cache but has been saved for the next request.

    :::term
    cache: [GET /assets/application-95bd4fe1de99c1cd91ec8e6f348a44bd.css] miss, store
    cache: [GET /assets/application-95fca227f3857c8ac9e7ba4ffed80386.js] miss, store
    cache: [GET /assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png] miss, store

    The 'fresh' indicates item was found in cache and will be served from cache
    `fresh` indicates item was found in cache and will be served from cache.

    :::term
    cache: [GET /assets/application-95bd4fe1de99c1cd91ec8e6f348a44bd.css] fresh
    cache: [GET /assets/application-95fca227f3857c8ac9e7ba4ffed80386.js] fresh
    cache: [GET /assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png] fresh

    Your Rails 3.1+ application is now configured to cache static assets using Memcached, freeing up dynos to perform dynamic application requests.

    ## Debugging

    Debugging
    ---------

    If a setting is not configured properly, you might see `miss` in your logs instead of `store` or `fresh` as seen below. Make sure that you're using a hard refresh to clear your browser cache while you're investigating the problem.
    If a setting is not configured properly, you might see `miss` in your logs instead of `store` or `fresh`.

    :::term
    cache: [GET /assets/application-95bd4fe1de99c1cd91ec8e6f348a44bd.css] miss
    cache: [GET /assets/application-95fca227f3857c8ac9e7ba4ffed80386.js] miss
    cache: [GET /assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png] miss


    When this happens, ensure that the Cache-Control header exists. It can be easier to debug locally so you need to set up your project to be run in production locally.

    :::term
    $ bundle exec rake db:create RAILS_ENV=production


    Compile assets with
    When this happens ensure that the Cache-Control header exists by using `curl` to inspect asset response headers.

    :::term
    $ bundle exec rake assets:precompile RAILS_ENV=production`

    You can then start your application locally in production

    :::term
    $ bundle exec rails s -e production

    Next copy one of your asset urls and `curl` it in the terminal using `-I` which will return the headers. It for an asset like `rails.png` it would look similar to this.

    :::term
    $ curl 'http://localhost:3000/assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png' -I


    The result should look something like below, where `Cache-Control` returns `public, max-age=2592000`

    :::term
    $ curl 'http://localhost:3000/assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png' -I

    $ curl -I 'http://rack-cache-demo.herokuapp.com/assets/shipit-72351bb81da0eca408d9bd8342f1b972.jpg'
    HTTP/1.1 200 OK
    Last-Modified: Sun, 18 Mar 2012 00:19:19 GMT
    Content-Type: image/png
    Age: 632
    Cache-Control: public, max-age=2592000
    Content-Length: 6646
    Date: Sun, 18 Mar 2012 21:27:07 GMT
    X-Content-Digest: 501d6b0108b930264e19f37cb8ee6c8222d4f30d
    Age: 689
    Content-length: 70522
    Etag: "72351bb81da0eca408d9bd8342f1b972"
    Last-Modified: Sun, 25 Mar 2012 01:51:21 GMT
    X-Rack-Cache: fresh
    Server: WEBrick/1.3.1 (Ruby/1.9.2/2011-07-09)
    Connection: Keep-Alive


    You can also ensure that you are seeing and `X-Rack-Cache` header indicating the status of your asset (fresh/store/miss).
    The response headers should contain `Cache-Control` with the value specific in the `config.static_cache_control` setting i.e.: `public, max-age=2592000`. Also confirm that you are seeing the `X-Rack-Cache` header indicating the status of your asset (fresh/store/miss).

    If you choose to use file storage for your entity store as demonstrated above, you can try deleting contents of the cache on disk, if your store is set to write to `file:tmp/cache/rack/body` you would go to your Rails root and remove all entries under `tmp/cache/rack/body`. When debugging using this step, do not forget to do a hard refresh in the browser.
    ### Inconsistent file versions

    If you modify a file and your server continues to serve the old file, check that you committed the file to your Git repository before deploying, and you can check to see if it exists in your compiled code by using `heroku run bash`
    If you modify a file and your server continues to serve the old file check that you committed it to your Git repository before deploying. You can check to see if it exists in your compiled code by using `heroku run bash` and listing the contents of the `public/assets` directory. This directory should contain the hashed asset file names.

    :::term
    $ heroku run bash
    Running bash attached to terminal... up, run.1
    ~ $ ls public/assets
    $ ls public/assets
    application-95bd4fe1de99c1cd91ec8e6f348a44bd.css application.css manifest.yml
    application-95bd4fe1de99c1cd91ec8e6f348a44bd.css.gz application.css.gz rails-782b548cc1ba7f898cdad2d9eb8420d2.png
    application-95fca227f3857c8ac9e7ba4ffed80386.js application.js rails.png
    application-95fca227f3857c8ac9e7ba4ffed80386.js.gz application.js.gz

    Don't forget to check if the file exists in your `manifest.yml`
    Also confirm that the file is listed in Rails' `manifest.yml`.

    :::term
    ~ $ cat public/assets/manifest.yml
    $ cat public/assets/manifest.yml
    rails.png: rails-782b548cc1ba7f898cdad2d9eb8420d2.png
    application.js: application-95fca227f3857c8ac9e7ba4ffed80386.js
    application.css: application-95bd4fe1de99c1cd91ec8e6f348a44bd.css

    If the file you're looking for does not show up try running `bundle exec rake assets:precompile RAILS_ENV=production` locally and ensure that it is in your own public/assets directory.
    If the file you're looking for does not show up try running `bundle exec rake assets:precompile RAILS_ENV=production` locally and ensure that it is in your own `public/assets` directory.
  2. schneems revised this gist Mar 26, 2012. 1 changed file with 6 additions and 0 deletions.
    6 changes: 6 additions & 0 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,12 @@

    Cedar does not include a reverse proxy cache such as Varnish, preferring to empower developers to choose the CDN or caching solution that best serves their needs. You can use `Rack::Cache` your Rails application to improve response time and decrease load. This is important if you're using the asset pipeline and serving static assets through your application.

    <p class="note" markdown="1">
    Sample code for this article's [reference application](https://github.com/heroku/rack-cache-demo) is available on
    GitHub and can be seen running at
    [http://rack-cache-demo.herokuapp.com/](http://rack-cache-demo.herokuapp.com/)
    </p>

    Using Rack::Cache
    -----------------

  3. schneems revised this gist Mar 24, 2012. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -12,7 +12,7 @@ Rack is written to be lightweight and flexible. If middleware can respond to a r

    Rack::Cache Storage
    -------------------
    Rack::Cache has two different storage areas, meta and entity store, which can be configured for use with three different storage backends ,file, heap, and memcache. The metastore in `Rack::Cache` keeps high level information about each cache entry including headers. The entitystore is the response body content, the actual object being returned through the server. Storing data in a file is slow but memory efficient; using heap means your process' memory will be used which is quicker, but can have serious impacts; and using memcache is the fastest option, but isn't well suited to store large objects.
    `Rack::Cache` has two different storage areas, meta and entity store, which can be configured for use with three different storage backends ,file, heap, and memcache. The metastore in `Rack::Cache` keeps high level information about each cache entry including headers. The entitystore is the response body content, the actual object being returned through the server. Storing data in a file is slow but memory efficient; using heap means your process' memory will be used which is quicker, but can have serious impacts; and using memcache is the fastest option, but isn't well suited to store large objects.


    Since the metastore is accessed frequently and is much smaller than the entitystore, you can use memcache for the backend, but to keep the amount of data stored in Memcache to a minimum, you can write the entitystore to a temporary file. Heap storage is not recommended. For more information on the entity and meta stores read more about [Rack Cache Storage](http://rtomayko.github.com/rack-cache/storage).
    @@ -95,7 +95,7 @@ Since these settings will tell `Rack::Cache` to store static elements for a very

    You also want to ensure that caching is turned on.

    # Enables caching including Rack::Cache
    # Enables caching including `Rack::Cache`
    config.action_controller.perform_caching = true


  4. schneems revised this gist Mar 24, 2012. 1 changed file with 80 additions and 19 deletions.
    99 changes: 80 additions & 19 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -1,35 +1,79 @@
    # Rack Cache on Cedar Rails 3.1+ Applications

    The Cedar stack gives us more flexibility but does not use Varnish or Nginx to help with caching and speed. To improve your application's response time and decrease load, you can use `Rack::Cache`. This is especially important if you're serving static assets through your application, such as images, stylsheets, and javascript through the Rails asset pipeline.

    Cedar does not include a reverse proxy cache such as Varnish, preferring to empower developers to choose the CDN or caching solution that best serves their needs. You can use `Rack::Cache` your Rails application to improve response time and decrease load. This is important if you're using the asset pipeline and serving static assets through your application.

    Using Rack::Cache
    -----------------
    Rails 3+ and other Ruby web frameworks, such as Sinatra, are built on top of Rack. At a high level, Rack works by taking a request and passing it through a series of steps, called middleware. Each Rack middleware performs some action, then passes the request to the next middleware in the rack. Eventually the request is properly formatted and it will reach your application. Rack is written to be lightweight and flexible. If a Rack middleware can respond to a request directly, then it doesn't have to hit your Rails application. This means that the request is retuned faster, and the overall load on the Rails application is decreased. `Rack::Cache` is middleware that enables HTTP caching on your application and can allow us to serve assets from a storage backend without requiring work from your main Rails application.

    `Rack::Cache` is rack middleware that enables HTTP caching on your application and can allow us to serve assets from a storage backend without requiring work from your main Rails application. Many Ruby web frameworks including Rails 3+ and Sinatra, are built on top of Rack. At a high level, Rack works by taking a request and passing it through a series of steps, called middleware. Each Rack middleware performs some action, then passes the request to the next middleware in the rack. Eventually the request is properly formatted and it will reach your application.

    Rack is written to be lightweight and flexible. If middleware can respond to a request directly, then it doesn't have to hit your Rails application. This means that the request is retuned faster, and the overall load on the Rails application is decreased.


    Rack::Cache Storage
    -------------------
    Rack::Cache has two different storage areas, meta and entity store, which can be configured for use with three different storage backends ,file, heap, and memcache. The metastore in `Rack::Cache` keeps high level information about each cache entry including headers. The entitystore is the response body content, the actual object being returned through the server. Storing data in a file is slow but memory efficient; using heap means your process' memory will be used which is quicker, but can have serious impacts; and using memcache is the fastest option, but isn't well suited to store large objects.


    Since the metastore is accessed frequently and is much smaller than the entitystore, you can use memcache for the backend, but to keep the amount of data stored in Memcache to a minimum, you can write the entitystore to a temporary file. Heap storage is not recommended. For more information on the entity and meta stores read more about [Rack Cache Storage](http://rtomayko.github.com/rack-cache/storage).


    Configure Memcache
    ------------------

    `Rack::Cache` can be used with different storage backends. By default `Rack::Cache` will try to use the file system, which is [read only](http://devcenter.heroku.com/articles/read-only-filesystem) on most of the Heroku platform, and on Cedar is [ephemeral](http://devcenter.heroku.com/articles/dyno-isolation#ephemeral_filesystem). Using memcache as a backend will allow you to persist your cache between deploys, across dynos, and will prevent a slow down on initial requests while warming the cache. Therefore Heroku recommends using Memcache with the Dalli gem as your `Rack::Cache` backend. Once you have [configured your application to use memcached](http://devcenter.heroku.com/articles/memcache), you can configure `Rack::Cache` for use in your application.
    <p class="callout" markdown="1">Configuring your application to use [Memcache](http://devcenter.heroku.com/articles/memcache)</p>

    Since you will use memcache in your `Rack::Cache` metastore, you will first need to add it to your project. Heroku recommends using Memcache with the Dalli gem as part of your `Rack::Cache` backend.


    You'll need to configure your Heroku application to use Memcache.

    :::term
    $ heroku addons:add memcache

    To get this running on your local machine you will need to have Memcache installed. You can install it using a tool such as homebrew.

    :::term
    $ brew install memcache

    At the end of installation homebrew will give you instructions on how to start Memcache manually and automatically on system start.

    You can then tell your application to use Dalli, Heroku's preferred Memcache client. In your Gemfile add

    :::term
    gem 'dalli'

    Then in your `config/application.rb` add

    :::term
    config.cache_store = :dalli_store

    If you start a Rails console session you should now have access to Memcache

    :::term
    > memcache = Dalli::Client.new
    > memcache.set('foo', 'bar')
    > memcache.get('foo') # => 'bar'

    Once you have [configured your application to use memcached](http://devcenter.heroku.com/articles/memcache), you can configure `Rack::Cache` for use in your application.


    Configure Rack::Cache to use Memcache on Rails 3.1+
    Configure Rack::Cache on Rails 3.1+
    -----------------------------------------------------

    For Rails 3.1+ you will need to modify your config/production.rb to tell rack_cache to use Memcache. By default Dalli will look for the proper environment variables when deployed to Heroku, and otherwise will default to localhost and the default port. If you want, rather than specifying an object, you can pass the connection string needed to talk to an external memcache server.
    For Rails 3.1+ you will need to modify your `config/production.rb` to tell rack_cache to use Memcache. By default `Dalli::Client.new` will look for the proper environment variables (ENV["MEMCACHE_SERVERS"]) when deployed to Heroku, and otherwise will default to localhost and the default port. If you want, rather than specifying an object, you can pass the connection string needed to talk to an external memcache server.


    :::term
    # This will tell `Rack::Cache` to use the default settings of Dalli, Heroku's prefered Memcache Gem
    # This will tell `Rack::Cache` to use the default settings of Dalli, Heroku's recommended Memcache Gem
    config.action_dispatch.rack_cache = {
    :metastore => Dalli::Client.new,
    :entitystore => Dalli::Client.new,
    :entitystore => 'file:tmp/cache/rack/body',
    :allow_reload => false }

    You can set the entitystore to write to a temporary file in the rails directory.

    Then you need to set `config.servestatic_assets` to true.
    To allow your application to serve static files from /public you need to set `config.servestatic_assets` to true.

    :::term
    # This will allow Action::Dispatch to serve files from /public when set to true
    @@ -43,7 +87,7 @@ Finally you need to tell the cache how long an item should stay in cache by sett
    config.static_cache_control = "public, max-age=2592000"


    Since these settings will tell `Rack::Cache` to store static elements for a very long time, it is important that you let your cache store know when you change a file. Typically cache invalidation can be very tricky, so to avoid that problem you can ensure that Rails generates a new file name every time you modify a file. This is done by using a hash digest such as MD5 on your files, tell Rails to do this automatically by setting `config.assets.digest`.
    Since these settings will tell `Rack::Cache` to store static elements for a very long time, it is important that you let your cache store know when you change a file. Typically cache invalidation can be very tricky, so to avoid that problem you can ensure that Rails generates a new file name every time you modify a file. This is done by using a hash digest such as MD5 on your files, tell Rails to do this automatically by setting `config.assets.digest` to true.

    :::term
    # Generate digests for assets URLs
    @@ -55,18 +99,16 @@ You also want to ensure that caching is turned on.
    config.action_controller.perform_caching = true


    Once all of this is set up correctly, you should see lines like this in your log.
    Once all of this is set up correctly, you should see cache lines in your production log. The 'miss, store' indicates that the item was not found in the cache but has been saved for the next request

    :::term
    # The item was not found in cache (miss) but has been saved for next request (store)

    cache: [GET /assets/application-95bd4fe1de99c1cd91ec8e6f348a44bd.css] miss, store
    cache: [GET /assets/application-95fca227f3857c8ac9e7ba4ffed80386.js] miss, store
    cache: [GET /assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png] miss, store

    :::term
    # The item was found in cache (fresh) and will be served from cache
    The 'fresh' indicates item was found in cache and will be served from cache

    :::term
    cache: [GET /assets/application-95bd4fe1de99c1cd91ec8e6f348a44bd.css] fresh
    cache: [GET /assets/application-95fca227f3857c8ac9e7ba4ffed80386.js] fresh
    cache: [GET /assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png] fresh
    @@ -76,21 +118,37 @@ Once all of this is set up correctly, you should see lines like this in your log
    Debugging
    ---------

    If a setting is not configured properly, you might see `miss` in your logs instead of `store` or `fresh` like this. Make sure that you're using a hard refresh to clear your browser cache while you're investigating the problem.
    If a setting is not configured properly, you might see `miss` in your logs instead of `store` or `fresh` as seen below. Make sure that you're using a hard refresh to clear your browser cache while you're investigating the problem.

    :::term
    cache: [GET /assets/application-95bd4fe1de99c1cd91ec8e6f348a44bd.css] miss
    cache: [GET /assets/application-95fca227f3857c8ac9e7ba4ffed80386.js] miss
    cache: [GET /assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png] miss


    When this happens, ensure that the Cache-Control header exists, copy one of your asset urls and `curl` it in the terminal using `-I` which will return the headers. If you can run your app in production mode locally it would look like this:
    When this happens, ensure that the Cache-Control header exists. It can be easier to debug locally so you need to set up your project to be run in production locally.

    :::term
    $ bundle exec rake db:create RAILS_ENV=production


    Compile assets with

    :::term
    $ bundle exec rake assets:precompile RAILS_ENV=production`

    You can then start your application locally in production

    :::term
    $ bundle exec rails s -e production

    Next copy one of your asset urls and `curl` it in the terminal using `-I` which will return the headers. It for an asset like `rails.png` it would look similar to this.

    :::term
    $ curl 'http://localhost:3000/assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png' -I


    The result should look something like this, where `Cache-Control` returns `public, max-age=2592000`
    The result should look something like below, where `Cache-Control` returns `public, max-age=2592000`

    :::term
    $ curl 'http://localhost:3000/assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png' -I
    @@ -107,8 +165,11 @@ The result should look something like this, where `Cache-Control` returns `publi
    Server: WEBrick/1.3.1 (Ruby/1.9.2/2011-07-09)
    Connection: Keep-Alive


    You can also ensure that you are seeing and `X-Rack-Cache` header indicating the status of your asset (fresh/store/miss).

    If you choose to use file storage for your entity store as demonstrated above, you can try deleting contents of the cache on disk, if your store is set to write to `file:tmp/cache/rack/body` you would go to your Rails root and remove all entries under `tmp/cache/rack/body`. When debugging using this step, do not forget to do a hard refresh in the browser.

    If you modify a file and your server continues to serve the old file, check that you committed the file to your Git repository before deploying, and you can check to see if it exists in your compiled code by using `heroku run bash`

    :::term
    @@ -128,4 +189,4 @@ Don't forget to check if the file exists in your `manifest.yml`
    application.js: application-95fca227f3857c8ac9e7ba4ffed80386.js
    application.css: application-95bd4fe1de99c1cd91ec8e6f348a44bd.css

    If the file you're looking for does not show up try running `rake assets:precompile` locally and ensure that it is in your own public/assets directory.
    If the file you're looking for does not show up try running `bundle exec rake assets:precompile RAILS_ENV=production` locally and ensure that it is in your own public/assets directory.
  5. schneems revised this gist Mar 18, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,7 @@ The Cedar stack gives us more flexibility but does not use Varnish or Nginx to h

    Using Rack::Cache
    -----------------
    Rails 3+ and other Ruby web frameworks, such as Sinatra, are built on top of Rack. At a high level, Rack works by taking a request and passing it through a series of steps, called middleware. Each Rack middleware performs some action then passes the request to the next level in the rack. Eventually the request is properly formatted and it will reach your Rails application, where your application can handle the request. Rack is written to be lightweight and flexible. If a Rack middleware can respond to a request directly from a Rack middleware, then it doesn't have to hit your Rails application. This means that the request is retuned faster, and the overall load on the Rails application is decreased. `Rack::Cache` is middleware that enables HTTP caching on your application and can allow us to serve assets from a storage backend without requiring work from your main Rails application.
    Rails 3+ and other Ruby web frameworks, such as Sinatra, are built on top of Rack. At a high level, Rack works by taking a request and passing it through a series of steps, called middleware. Each Rack middleware performs some action, then passes the request to the next middleware in the rack. Eventually the request is properly formatted and it will reach your application. Rack is written to be lightweight and flexible. If a Rack middleware can respond to a request directly, then it doesn't have to hit your Rails application. This means that the request is retuned faster, and the overall load on the Rails application is decreased. `Rack::Cache` is middleware that enables HTTP caching on your application and can allow us to serve assets from a storage backend without requiring work from your main Rails application.


    Configure Memcache
  6. schneems revised this gist Mar 18, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -123,7 +123,7 @@ If you modify a file and your server continues to serve the old file, check that
    Don't forget to check if the file exists in your `manifest.yml`

    :::term
    ~$ cat public/assets/manifest.yml
    ~ $ cat public/assets/manifest.yml
    rails.png: rails-782b548cc1ba7f898cdad2d9eb8420d2.png
    application.js: application-95fca227f3857c8ac9e7ba4ffed80386.js
    application.css: application-95bd4fe1de99c1cd91ec8e6f348a44bd.css
  7. schneems revised this gist Mar 18, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -123,7 +123,7 @@ If you modify a file and your server continues to serve the old file, check that
    Don't forget to check if the file exists in your `manifest.yml`

    :::term
    $~ cat public/assets/manifest.yml
    ~$ cat public/assets/manifest.yml
    rails.png: rails-782b548cc1ba7f898cdad2d9eb8420d2.png
    application.js: application-95fca227f3857c8ac9e7ba4ffed80386.js
    application.css: application-95bd4fe1de99c1cd91ec8e6f348a44bd.css
  8. schneems revised this gist Mar 18, 2012. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -31,9 +31,9 @@ For Rails 3.1+ you will need to modify your config/production.rb to tell rack_ca

    Then you need to set `config.servestatic_assets` to true.

    :::term
    # This will allow Action::Dispatch to serve files from /public when set to true
    config.serve_static_assets = true
    :::term
    # This will allow Action::Dispatch to serve files from /public when set to true
    config.serve_static_assets = true


    Finally you need to tell the cache how long an item should stay in cache by setting the Cache-Control headers. Without a Cache-Control header, static files will not be stored by `Rack::Cache`
  9. schneems revised this gist Mar 18, 2012. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -78,10 +78,10 @@ Debugging

    If a setting is not configured properly, you might see `miss` in your logs instead of `store` or `fresh` like this. Make sure that you're using a hard refresh to clear your browser cache while you're investigating the problem.

    :::term
    cache: [GET /assets/application-95bd4fe1de99c1cd91ec8e6f348a44bd.css] miss
    cache: [GET /assets/application-95fca227f3857c8ac9e7ba4ffed80386.js] miss
    cache: [GET /assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png] miss
    :::term
    cache: [GET /assets/application-95bd4fe1de99c1cd91ec8e6f348a44bd.css] miss
    cache: [GET /assets/application-95fca227f3857c8ac9e7ba4ffed80386.js] miss
    cache: [GET /assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png] miss


    When this happens, ensure that the Cache-Control header exists, copy one of your asset urls and `curl` it in the terminal using `-I` which will return the headers. If you can run your app in production mode locally it would look like this:
  10. schneems revised this gist Mar 18, 2012. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,7 @@
    The Cedar stack gives us more flexibility but does not use Varnish or Nginx to help with caching and speed. To improve your application's response time and decrease load, you can use `Rack::Cache`. This is especially important if you're serving static assets through your application, such as images, stylsheets, and javascript through the Rails asset pipeline.


    Using `Rack::Cache`
    Using Rack::Cache
    -----------------
    Rails 3+ and other Ruby web frameworks, such as Sinatra, are built on top of Rack. At a high level, Rack works by taking a request and passing it through a series of steps, called middleware. Each Rack middleware performs some action then passes the request to the next level in the rack. Eventually the request is properly formatted and it will reach your Rails application, where your application can handle the request. Rack is written to be lightweight and flexible. If a Rack middleware can respond to a request directly from a Rack middleware, then it doesn't have to hit your Rails application. This means that the request is retuned faster, and the overall load on the Rails application is decreased. `Rack::Cache` is middleware that enables HTTP caching on your application and can allow us to serve assets from a storage backend without requiring work from your main Rails application.

    @@ -15,7 +15,7 @@ Configure Memcache



    Configure `Rack::Cache` to use Memcache on Rails 3.1+
    Configure Rack::Cache to use Memcache on Rails 3.1+
    -----------------------------------------------------

    For Rails 3.1+ you will need to modify your config/production.rb to tell rack_cache to use Memcache. By default Dalli will look for the proper environment variables when deployed to Heroku, and otherwise will default to localhost and the default port. If you want, rather than specifying an object, you can pass the connection string needed to talk to an external memcache server.
    @@ -76,7 +76,7 @@ Once all of this is set up correctly, you should see lines like this in your log
    Debugging
    ---------

    If a setting is not configured properly, you might see `miss` in your logs instead of `store` or `fresh` like this. Make sure that you're using a hard refresh to clear your browser cache while you're investigating the problem.
    If a setting is not configured properly, you might see `miss` in your logs instead of `store` or `fresh` like this. Make sure that you're using a hard refresh to clear your browser cache while you're investigating the problem.

    :::term
    cache: [GET /assets/application-95bd4fe1de99c1cd91ec8e6f348a44bd.css] miss
  11. schneems renamed this gist Mar 18, 2012. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  12. schneems created this gist Mar 18, 2012.
    131 changes: 131 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,131 @@
    # Rack Cache on Cedar Rails 3.1+ Applications

    The Cedar stack gives us more flexibility but does not use Varnish or Nginx to help with caching and speed. To improve your application's response time and decrease load, you can use `Rack::Cache`. This is especially important if you're serving static assets through your application, such as images, stylsheets, and javascript through the Rails asset pipeline.


    Using `Rack::Cache`
    -----------------
    Rails 3+ and other Ruby web frameworks, such as Sinatra, are built on top of Rack. At a high level, Rack works by taking a request and passing it through a series of steps, called middleware. Each Rack middleware performs some action then passes the request to the next level in the rack. Eventually the request is properly formatted and it will reach your Rails application, where your application can handle the request. Rack is written to be lightweight and flexible. If a Rack middleware can respond to a request directly from a Rack middleware, then it doesn't have to hit your Rails application. This means that the request is retuned faster, and the overall load on the Rails application is decreased. `Rack::Cache` is middleware that enables HTTP caching on your application and can allow us to serve assets from a storage backend without requiring work from your main Rails application.


    Configure Memcache
    ------------------

    `Rack::Cache` can be used with different storage backends. By default `Rack::Cache` will try to use the file system, which is [read only](http://devcenter.heroku.com/articles/read-only-filesystem) on most of the Heroku platform, and on Cedar is [ephemeral](http://devcenter.heroku.com/articles/dyno-isolation#ephemeral_filesystem). Using memcache as a backend will allow you to persist your cache between deploys, across dynos, and will prevent a slow down on initial requests while warming the cache. Therefore Heroku recommends using Memcache with the Dalli gem as your `Rack::Cache` backend. Once you have [configured your application to use memcached](http://devcenter.heroku.com/articles/memcache), you can configure `Rack::Cache` for use in your application.



    Configure `Rack::Cache` to use Memcache on Rails 3.1+
    -----------------------------------------------------

    For Rails 3.1+ you will need to modify your config/production.rb to tell rack_cache to use Memcache. By default Dalli will look for the proper environment variables when deployed to Heroku, and otherwise will default to localhost and the default port. If you want, rather than specifying an object, you can pass the connection string needed to talk to an external memcache server.


    :::term
    # This will tell `Rack::Cache` to use the default settings of Dalli, Heroku's prefered Memcache Gem
    config.action_dispatch.rack_cache = {
    :metastore => Dalli::Client.new,
    :entitystore => Dalli::Client.new,
    :allow_reload => false }


    Then you need to set `config.servestatic_assets` to true.

    :::term
    # This will allow Action::Dispatch to serve files from /public when set to true
    config.serve_static_assets = true


    Finally you need to tell the cache how long an item should stay in cache by setting the Cache-Control headers. Without a Cache-Control header, static files will not be stored by `Rack::Cache`

    :::term
    # This will set Cache-Control headers used by browsers
    config.static_cache_control = "public, max-age=2592000"


    Since these settings will tell `Rack::Cache` to store static elements for a very long time, it is important that you let your cache store know when you change a file. Typically cache invalidation can be very tricky, so to avoid that problem you can ensure that Rails generates a new file name every time you modify a file. This is done by using a hash digest such as MD5 on your files, tell Rails to do this automatically by setting `config.assets.digest`.

    :::term
    # Generate digests for assets URLs
    config.assets.digest = true

    You also want to ensure that caching is turned on.

    # Enables caching including Rack::Cache
    config.action_controller.perform_caching = true


    Once all of this is set up correctly, you should see lines like this in your log.

    :::term
    # The item was not found in cache (miss) but has been saved for next request (store)

    cache: [GET /assets/application-95bd4fe1de99c1cd91ec8e6f348a44bd.css] miss, store
    cache: [GET /assets/application-95fca227f3857c8ac9e7ba4ffed80386.js] miss, store
    cache: [GET /assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png] miss, store

    :::term
    # The item was found in cache (fresh) and will be served from cache

    cache: [GET /assets/application-95bd4fe1de99c1cd91ec8e6f348a44bd.css] fresh
    cache: [GET /assets/application-95fca227f3857c8ac9e7ba4ffed80386.js] fresh
    cache: [GET /assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png] fresh



    Debugging
    ---------

    If a setting is not configured properly, you might see `miss` in your logs instead of `store` or `fresh` like this. Make sure that you're using a hard refresh to clear your browser cache while you're investigating the problem.

    :::term
    cache: [GET /assets/application-95bd4fe1de99c1cd91ec8e6f348a44bd.css] miss
    cache: [GET /assets/application-95fca227f3857c8ac9e7ba4ffed80386.js] miss
    cache: [GET /assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png] miss


    When this happens, ensure that the Cache-Control header exists, copy one of your asset urls and `curl` it in the terminal using `-I` which will return the headers. If you can run your app in production mode locally it would look like this:

    :::term
    $ curl 'http://localhost:3000/assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png' -I


    The result should look something like this, where `Cache-Control` returns `public, max-age=2592000`

    :::term
    $ curl 'http://localhost:3000/assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png' -I

    HTTP/1.1 200 OK
    Last-Modified: Sun, 18 Mar 2012 00:19:19 GMT
    Content-Type: image/png
    Cache-Control: public, max-age=2592000
    Content-Length: 6646
    Date: Sun, 18 Mar 2012 21:27:07 GMT
    X-Content-Digest: 501d6b0108b930264e19f37cb8ee6c8222d4f30d
    Age: 689
    X-Rack-Cache: fresh
    Server: WEBrick/1.3.1 (Ruby/1.9.2/2011-07-09)
    Connection: Keep-Alive

    You can also ensure that you are seeing and `X-Rack-Cache` header indicating the status of your asset (fresh/store/miss).

    If you modify a file and your server continues to serve the old file, check that you committed the file to your Git repository before deploying, and you can check to see if it exists in your compiled code by using `heroku run bash`

    :::term
    $ heroku run bash
    Running bash attached to terminal... up, run.1
    ~ $ ls public/assets
    application-95bd4fe1de99c1cd91ec8e6f348a44bd.css application.css manifest.yml
    application-95bd4fe1de99c1cd91ec8e6f348a44bd.css.gz application.css.gz rails-782b548cc1ba7f898cdad2d9eb8420d2.png
    application-95fca227f3857c8ac9e7ba4ffed80386.js application.js rails.png
    application-95fca227f3857c8ac9e7ba4ffed80386.js.gz application.js.gz

    Don't forget to check if the file exists in your `manifest.yml`

    :::term
    $~ cat public/assets/manifest.yml
    rails.png: rails-782b548cc1ba7f898cdad2d9eb8420d2.png
    application.js: application-95fca227f3857c8ac9e7ba4ffed80386.js
    application.css: application-95bd4fe1de99c1cd91ec8e6f348a44bd.css

    If the file you're looking for does not show up try running `rake assets:precompile` locally and ensure that it is in your own public/assets directory.