Created
March 18, 2012 23:13
-
-
Save schneems/2084471 to your computer and use it in GitHub Desktop.
Revisions
-
schneems revised this gist
Apr 3, 2012 . 1 changed file with 94 additions and 99 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,198 +1,193 @@ <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. 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> ## 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. 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 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. 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. <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. ## Install Memcache locally <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 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 memcached At the end of installation homebrew will give you instructions on how to start Memcache manually and automatically on system start. ## Configure Rails cache-store 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' 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`. :::ruby config.cache_store = :dalli_store 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' Once you've configured your application to use memcached it's now time to configure Rack::Cache. ## Configure Rack::Cache Modify your `config/environments/production.rb` environment file to specify the appropriate storage backends for Rails' built-in Rack::Cache integration. :::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> ## Serve static assets <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 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. :::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` :::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. :::ruby config.assets.digest = true You also want to confirm that caching is turned on in production. :::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> 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 `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 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 by using `curl` to inspect asset response headers. :::term $ curl -I 'http://rack-cache-demo.herokuapp.com/assets/shipit-72351bb81da0eca408d9bd8342f1b972.jpg' HTTP/1.1 200 OK Age: 632 Cache-Control: public, max-age=2592000 Content-length: 70522 Etag: "72351bb81da0eca408d9bd8342f1b972" Last-Modified: Sun, 25 Mar 2012 01:51:21 GMT X-Rack-Cache: fresh 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). ### Inconsistent file versions 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 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 Also confirm that the file is listed in Rails' `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 `bundle exec rake assets:precompile RAILS_ENV=production` locally and ensure that it is in your own `public/assets` directory. -
schneems revised this gist
Mar 26, 2012 . 1 changed file with 6 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 ----------------- -
schneems revised this gist
Mar 24, 2012 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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. 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` config.action_controller.perform_caching = true -
schneems revised this gist
Mar 24, 2012 . 1 changed file with 80 additions and 19 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,35 +1,79 @@ # Rack Cache on Cedar Rails 3.1+ Applications 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 ----------------- `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 ------------------ <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 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::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 recommended Memcache Gem config.action_dispatch.rack_cache = { :metastore => 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. 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` 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 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 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 :::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` 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. 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 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 `bundle exec rake assets:precompile RAILS_ENV=production` locally and ensure that it is in your own public/assets directory. -
schneems revised this gist
Mar 18, 2012 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 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 -
schneems revised this gist
Mar 18, 2012 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 rails.png: rails-782b548cc1ba7f898cdad2d9eb8420d2.png application.js: application-95fca227f3857c8ac9e7ba4ffed80386.js application.css: application-95bd4fe1de99c1cd91ec8e6f348a44bd.css -
schneems revised this gist
Mar 18, 2012 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 rails.png: rails-782b548cc1ba7f898cdad2d9eb8420d2.png application.js: application-95fca227f3857c8ac9e7ba4ffed80386.js application.css: application-95bd4fe1de99c1cd91ec8e6f348a44bd.css -
schneems revised this gist
Mar 18, 2012 . 1 changed file with 3 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 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` -
schneems revised this gist
Mar 18, 2012 . 1 changed file with 4 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 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: -
schneems revised this gist
Mar 18, 2012 . 1 changed file with 3 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 ----------------- 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+ ----------------------------------------------------- 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. :::term cache: [GET /assets/application-95bd4fe1de99c1cd91ec8e6f348a44bd.css] miss -
schneems renamed this gist
Mar 18, 2012 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
schneems created this gist
Mar 18, 2012 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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.