1
h2. Caching with Rails: An overview
3
Everyone caches. This guide will teach you what you need to know about
4
avoiding that expensive round-trip to your database and returning what you
5
need to return to those hungry web clients in the shortest time possible.
7
After reading this guide, you should be able to use and configure:
9
* Page, action, and fragment caching
11
* Alternative cache stores
12
* Conditional GET support
18
This is an introduction to the three types of caching techniques that Rails
19
provides by default without the use of any third party plugins.
21
To get started make sure +config.action_controller.perform_caching+ is set
22
to +true+ for your environment. This flag is normally set in the
23
corresponding config/environments/*.rb. By default, caching is disabled for development and test, and enabled for production.
26
config.action_controller.perform_caching = true
31
Page caching is a Rails mechanism which allows the request for a generated
32
page to be fulfilled by the webserver, without ever having to go through the
33
Rails stack at all. Obviously, this is super-fast. Unfortunately, it can't be
34
applied to every situation (such as pages that need authentication) and since
35
the webserver is literally just serving a file from the filesystem, cache
36
expiration is an issue that needs to be dealt with.
38
So, how do you enable this super-fast cache behavior? Suppose you
39
have a controller called +ProductsController+ and an +index+ action that lists all
40
the products. You could enable caching for this action like this:
43
class ProductsController < ActionController
52
The first time anyone requests products/index, Rails will generate a file
53
called +index.html+. If a web server see this file, it will be served in response to the
54
next request for products/index, without your Rails application being called.
56
By default, the page cache directory is set to Rails.public_path (which is
57
usually set to +File.join(self.root, "public")+ - that is, the public directory under your Rails application's root). This can be configured by
58
changing the configuration setting +config.action_controller.page_cache_directory+.
59
Changing the default from /public helps avoid naming conflicts, since you may
60
want to put other static html in /public, but changing this will require web
61
server reconfiguration to let the web server know where to serve the cached
64
The page caching mechanism will automatically add a +.html+ extension to
65
requests for pages that do not have an extension to make it easy for the
66
webserver to find those pages. This can be configured by changing the
67
configuration setting +config.action_controller.page_cache_extension+.
69
In order to expire this page when a new product is added you could extend the products controller like this:
72
class ProductsController < ActionController
79
expire_page :action => :index
85
If you want a more complicated expiration scheme, you can use cache sweepers
86
to expire cached objects when things change. This is covered in the section on Sweepers.
88
Note: Page caching ignores all parameters, so /products/list?page=1 will be written out to the filesystem as /products/list.html and if someone requests /products/list?page=2, they will be returned the same result as page=1. Be careful when page caching GET parameters in the URL!
92
One of the issues with page caching is that you cannot use it for pages that
93
require checking code to determine whether the user should be permitted access. This is where Action Caching comes in.
94
action caching works like page caching except for the fact that the incoming
95
web request does go from the web server to the Rails stack and Action Pack so
96
that before filters can be run on it before the cache is served. This allows you to use
97
authentication and other restrictions while still serving the
98
result of the output from a cached copy.
100
Clearing the cache works in the exact same way as with page caching.
102
Let's say you only wanted authenticated users to edit or create a Product
103
object, but still cache those pages:
106
class ProductsController < ActionController
108
before_filter :authenticate, :only => [ :edit, :create ]
115
expire_page :action => :index
116
expire_action :action => :edit
124
You can also use +:if+ (or +:unless+) to pass a Proc that specifies when the
125
action should be cached. Also, you can use +:layout => false+ to cache without
126
layout so that dynamic information in the layout such as the name of the logged-in user
127
or the number of items in the cart can be left uncached. This feature is
128
available as of Rails 2.2.
130
You can modify the default action cache path by passing a +:cache_path+ option.
131
This will be passed directly to +ActionCachePath.path_for+. This is handy for
132
actions with multiple possible routes that should be cached differently. If
133
a block is given, it is called with the current controller instance.
135
Finally, if you are using memcached, you can also pass +:expires_in+. In fact,
136
all parameters not used by +caches_action+ are sent to the underlying cache
141
Life would be perfect if we could get away with caching the entire contents of
142
a page or action and serving it out to the world. Unfortunately, dynamic web
143
applications usually build pages with a variety of components not all of which
144
have the same caching characteristics. In order to address such a dynamically
145
created page where different parts of the page need to be cached and expired
146
differently Rails provides a mechanism called Fragment Caching.
148
Fragment Caching allows a fragment of view logic to be wrapped in a cache
149
block and served out of the cache store when the next request comes in.
151
As an example, if you wanted to show all the orders placed on your website
152
in real time and didn't want to cache that part of the page, but did want
153
to cache the part of the page which lists all products available, you
154
could use this piece of code:
157
<% Order.find_recent.each do |o| %>
158
<%= o.buyer.name %> bought <% o.product.name %>
162
All available products:
163
<% Product.find(:all).each do |p| %>
164
<%= link_to p.name, product_url(p) %>
169
The cache block in our example will bind to the action that called it and is
170
written out to the same place as the action cache, which means that if you
171
want to cache multiple fragments per action, you should provide an +action_suffix+ to the cache call:
174
<% cache(:action => 'recent', :action_suffix => 'all_prods') do %>
175
All available products:
178
You can expire the cache using the +expire_fragment+ method, like so:
181
expire_fragment(:controller => 'products', :action => 'recent',
182
:action_suffix => 'all_prods)
185
If you don't want the cache block to bind to the action that called it, you can
186
also use globally keyed fragments. To do this, call the +cache+ method with a key, like
191
['all_available_products', @latest_product.created_at].join(':')) do %>
192
All available products:
196
This fragment is then available to all actions in the +ProductsController+ using
197
the key and can be expired the same way:
200
expire_fragment(:key =>
201
['all_available_products', @latest_product.created_at].join(':'))
206
Cache sweeping is a mechanism which allows you to get around having a ton of
207
+expire_{page,action,fragment}+ calls in your code. It does this by moving all the work
208
required to expire cached content into na +ActionController::Caching::Sweeper+
209
class. This class is an Observer that looks for changes to an object via callbacks,
210
and when a change occurs it expires the caches associated with that object in
211
an around or after filter.
213
Continuing with our Product controller example, we could rewrite it with a
217
class StoreSweeper < ActionController::Caching::Sweeper
218
# This sweeper is going to keep an eye on the Product model
221
# If our sweeper detects that a Product was created call this
222
def after_create(product)
223
expire_cache_for(product)
226
# If our sweeper detects that a Product was updated call this
227
def after_update(product)
228
expire_cache_for(product)
231
# If our sweeper detects that a Product was deleted call this
232
def after_destroy(product)
233
expire_cache_for(product)
237
def expire_cache_for(record)
238
# Expire the list page now that we added a new product
239
expire_page(:controller => '#{record}', :action => 'list')
242
expire_fragment(:controller => '#{record}',
243
:action => 'recent', :action_suffix => 'all_products')
248
The sweeper has to be added to the controller that will use it. So, if we wanted to expire the cached content for the
249
list and edit actions when the create action was called, we could do the
253
class ProductsController < ActionController
255
before_filter :authenticate, :only => [ :edit, :create ]
258
cache_sweeper :store_sweeper, :only => [ :create ]
263
expire_page :action => :list
264
expire_action :action => :edit
274
Query caching is a Rails feature that caches the result set returned by each
275
query. If Rails encounters the same query again during the current request, it
276
will used the cached result set as opposed to running the query against the
282
class ProductsController < ActionController
284
before_filter :authenticate, :only => [ :edit, :create ]
287
cache_sweeper :store_sweeper, :only => [ :create ]
295
# Run the same query again
300
expire_page :action => :list
301
expire_action :action => :edit
309
In the 'list' action above, the result set returned by the first
310
Product.find(:all) will be cached and will be used to avoid querying the
311
database again the second time that finder is called.
313
Query caches are created at the start of an action and destroyed at the end of
314
that action and thus persist only for the duration of the action.
318
Rails (as of 2.1) provides different stores for the cached data created by action and
319
fragment caches. Page caches are always stored on disk.
321
Rails 2.1 and above provide ActiveSupport::Cache::Store which can be used to
322
cache strings. Some cache store implementations, like MemoryStore, are able to
323
cache arbitrary Ruby objects, but don't count on every cache store to be able
326
The default cache stores provided with Rails include:
328
1) ActiveSupport::Cache::MemoryStore: A cache store implementation which stores
329
everything into memory in the same process. If you're running multiple Ruby on
330
Rails server processes (which is the case if you're using mongrel_cluster or
331
Phusion Passenger), then this means that your Rails server process instances
332
won't be able to share cache data with each other. If your application never
333
performs manual cache item expiry (e.g. when you‘re using generational cache
334
keys), then using +MemoryStore+ is ok. Otherwise, consider carefully whether you
335
should be using this cache store.
337
+MemoryStore+ is not only able to store strings, but also arbitrary Ruby objects.
339
+MemoryStore+ is not thread-safe. Use +SynchronizedMemoryStore+ instead if you
344
ActionController::Base.cache_store = :memory_store
347
2) ActiveSupport::Cache::FileStore: Cached data is stored on the disk. This is
348
the default store and the default path for this store is: /tmp/cache. Works
349
well for all types of environments and allows all processes running from the
350
same application directory to access the cached content. If /tmp/cache does not
351
exist, the default store becomes MemoryStore.
354
ActionController::Base.cache_store = :file_store, "/path/to/cache/directory"
357
3) ActiveSupport::Cache::DRbStore: Cached data is stored in a separate shared
358
DRb process that all servers communicate with. This works for all environments
359
and only keeps one cache around for all processes, but requires that you run
360
and manage a separate DRb process.
363
ActionController::Base.cache_store = :drb_store, "druby://localhost:9192"
366
4) MemCached store: Works like DRbStore, but uses Danga's MemCache instead.
367
Rails uses the bundled memcached-client gem by default. This is currently the
368
most popular cache store for production websites.
372
* Clustering and load balancing. One can specify multiple memcached servers,
373
and MemCacheStore will load balance between all available servers. If a
374
server goes down, then MemCacheStore will ignore it until it goes back
376
* Time-based expiry support. See +write+ and the +:expires_in+ option.
377
* Per-request in memory cache for all communication with the MemCache server(s).
379
It also accepts a hash of additional options:
381
* +:namespace+- specifies a string that will automatically be prepended to keys when accessing the memcached store.
382
* +:readonly+- a boolean value that when set to true will make the store read-only, with an error raised on any attempt to write.
383
* +:multithread+ - a boolean value that adds thread safety to read/write operations - it is unlikely you'll need to use this option as the Rails threadsafe! method offers the same functionality.
385
The read and write methods of the MemCacheStore accept an options hash too.
386
When reading you can specify +:raw => true+ to prevent the object being
388
(by default this is false which means the raw value in the cache is passed to
389
+Marshal.load+ before being returned to you.)
391
When writing to the cache it is also possible to specify +:raw => true+. This means
392
that the value is not passed to +Marshal.dump+ before being stored in the cache (by
393
default this is false).
395
The write method also accepts an +:unless_exist+ flag which determines whether
396
the memcached add (when true) or set (when false) method is used to store the
397
item in the cache and an +:expires_in+ option that specifies the time-to-live
398
for the cached item in seconds.
402
ActionController::Base.cache_store = :mem_cache_store, "localhost"
405
5) ActiveSupport::Cache::SynchronizedMemoryStore: Like ActiveSupport::Cache::MemoryStore but thread-safe.
409
ActionController::Base.cache_store = :synchronized_memory_store
412
6) ActiveSupport::Cache::CompressedMemCacheStore: Works just like the regular
413
MemCacheStore but uses GZip to decompress/compress on read/write.
417
ActionController::Base.cache_store = :compressed_mem_cache_store, "localhost"
420
7) Custom store: You can define your own cache store (new in Rails 2.1)
424
ActionController::Base.cache_store = MyOwnStore.new("parameter")
427
NOTE: +config.cache_store+ can be used in place of
428
+ActionController::Base.cache_store+ in the +Rails::Initializer.run+ block in
431
In addition to all of this, Rails also adds the +ActiveRecord::Base#cache_key+
432
method that generates a key using the class name, id and updated_at timestamp
438
Rails.cache.read("city") # => nil
439
Rails.cache.write("city", "Duckburgh")
440
Rails.cache.read("city") # => "Duckburgh"
443
h3. Conditional GET Support
445
Conditional GETs are a feature of the HTTP specification that provide a way for web
446
servers to tell browsers that the response to a GET request hasn't changed
447
since the last request and can be safely pulled from the browser cache.
449
They work by using the HTTP_IF_NONE_MATCH and HTTP_IF_MODIFIED_SINCE headers to
450
pass back and forth both a unique content identifier and the timestamp of when
451
the content was last changed. If the browser makes a request where the content
452
identifier (etag) or last modified since timestamp matches the server’s version
453
then the server only needs to send back an empty response with a not modified
456
It is the server's (i.e. our) responsibility to look for a last modified
457
timestamp and the if-none-match header and determine whether or not to send
458
back the full response. With conditional-get support in rails this is a pretty
462
class ProductsController < ApplicationController
465
@product = Product.find(params[:id])
467
# If the request is stale according to the given timestamp and etag value
468
# (i.e. it needs to be processed again) then execute this block
469
if stale?(:last_modified => @product.updated_at.utc, :etag => @product)
470
respond_to do |wants|
471
# ... normal response processing
475
# If the request is fresh (i.e. it's not modified) then you don't need to do
476
# anything. The default render checks for this using the parameters
477
# used in the previous call to stale? and will automatically send a
478
# :not_modified. So that's it, you're done.
482
If you don't have any special response processing and are using the default
483
rendering mechanism (i.e. you're not using respond_to or calling render
484
yourself) then you’ve got an easy helper in fresh_when:
487
class ProductsController < ApplicationController
489
# This will automatically send back a :not_modified if the request is fresh,
490
# and will render the default template (product.*) if it's stale.
493
@product = Product.find(params[:id])
494
fresh_when :last_modified => @product.published_at.utc, :etag => @article
501
Along with the built-in mechanisms outlined above, a number of excellent
502
plugins exist to help with finer grained control over caching. These include
503
Chris Wanstrath's excellent cache_fu plugin (more info "here": http://errtheblog.com/posts/57-kickin-ass-w-cachefu) and Evan Weaver's
504
interlock plugin (more info "here": http://blog.evanweaver.com/articles/2007/12/13/better-rails-caching/). Both
505
of these plugins play nice with memcached and are a must-see for anyone
506
seriously considering optimizing their caching needs.
508
Also the new "Cache money":http://github.com/nkallen/cache-money/tree/master plugin is supposed to be mad cool.
512
* "RailsEnvy, Rails Caching Tutorial, Part 1":http://www.railsenvy.com/2007/2/28/rails-caching-tutorial
513
* "RailsEnvy, Rails Caching Tutorial, Part 1":http://www.railsenvy.com/2007/3/20/ruby-on-rails-caching-tutorial-part-2
514
* "ActiveSupport::Cache documentation":http://api.rubyonrails.org/classes/ActiveSupport/Cache.html
515
* "Rails 2.1 integrated caching tutorial":http://thewebfellas.com/blog/2008/6/9/rails-2-1-now-with-better-integrated-caching
519
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/10-guide-to-caching
521
* February 22, 2009: Beefed up the section on cache_stores
522
* December 27, 2008: Typo fixes
523
* November 23, 2008: Incremental updates with various suggested changes and formatting cleanup
524
* September 15, 2008: Initial version by Aditya Chadha