~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/railties/guides/source/caching_with_rails.textile

  • Committer: Michael Forrest
  • Date: 2010-10-15 16:28:50 UTC
  • Revision ID: michael.forrest@canonical.com-20101015162850-tj2vchanv0kr0dun
refroze gems

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
h2. Caching with Rails: An overview
 
2
 
 
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.
 
6
 
 
7
After reading this guide, you should be able to use and configure:
 
8
 
 
9
* Page, action, and fragment caching
 
10
* Sweepers
 
11
* Alternative cache stores
 
12
* Conditional GET support
 
13
 
 
14
endprologue.
 
15
 
 
16
h3. Basic Caching
 
17
 
 
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.
 
20
 
 
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.
 
24
 
 
25
<ruby>
 
26
config.action_controller.perform_caching = true
 
27
</ruby>
 
28
 
 
29
h4. Page Caching
 
30
 
 
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.
 
37
 
 
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:
 
41
 
 
42
<ruby>
 
43
class ProductsController < ActionController
 
44
 
 
45
  caches_page :index
 
46
 
 
47
  def index; end
 
48
 
 
49
end
 
50
</ruby>
 
51
 
 
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.
 
55
 
 
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
 
62
files from.
 
63
 
 
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+.
 
68
 
 
69
In order to expire this page when a new product is added you could extend the products controller like this:
 
70
 
 
71
<ruby>
 
72
class ProductsController < ActionController
 
73
 
 
74
  caches_page :index
 
75
 
 
76
  def index; end
 
77
 
 
78
  def create
 
79
    expire_page :action => :index
 
80
  end
 
81
 
 
82
end
 
83
</ruby>
 
84
 
 
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.
 
87
 
 
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!
 
89
 
 
90
h4. Action Caching
 
91
 
 
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.
 
99
 
 
100
Clearing the cache works in the exact same way as with page caching.
 
101
 
 
102
Let's say you only wanted authenticated users to edit or create a Product
 
103
object, but still cache those pages:
 
104
 
 
105
<ruby>
 
106
class ProductsController < ActionController
 
107
 
 
108
  before_filter :authenticate, :only => [ :edit, :create ]
 
109
  caches_page :index
 
110
  caches_action :edit
 
111
 
 
112
  def index; end
 
113
 
 
114
  def create
 
115
    expire_page :action => :index
 
116
    expire_action :action => :edit
 
117
  end
 
118
 
 
119
  def edit; end
 
120
 
 
121
end
 
122
</ruby>
 
123
 
 
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.
 
129
 
 
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.  
 
134
 
 
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
 
137
store. 
 
138
 
 
139
h4. Fragment Caching
 
140
 
 
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.
 
147
 
 
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.
 
150
 
 
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:
 
155
 
 
156
<ruby>
 
157
<% Order.find_recent.each do |o| %>
 
158
  <%= o.buyer.name %> bought <% o.product.name %>
 
159
<% end %>
 
160
 
 
161
<% cache do %>
 
162
  All available products:
 
163
  <% Product.find(:all).each do |p| %>
 
164
    <%= link_to p.name, product_url(p) %>
 
165
  <% end %>
 
166
<% end %>
 
167
</ruby>
 
168
 
 
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:
 
172
 
 
173
<ruby>
 
174
<% cache(:action => 'recent', :action_suffix => 'all_prods') do %>
 
175
  All available products:
 
176
</ruby>
 
177
 
 
178
You can expire the cache using the +expire_fragment+ method, like so:
 
179
 
 
180
<ruby>
 
181
expire_fragment(:controller => 'products', :action => 'recent', 
 
182
  :action_suffix => 'all_prods)
 
183
</ruby>
 
184
 
 
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
 
187
so:
 
188
 
 
189
<ruby>
 
190
<% cache(:key => 
 
191
  ['all_available_products', @latest_product.created_at].join(':')) do %>
 
192
  All available products:
 
193
<% end %>
 
194
</ruby>
 
195
 
 
196
This fragment is then available to all actions in the +ProductsController+ using
 
197
the key and can be expired the same way:
 
198
 
 
199
<ruby>
 
200
expire_fragment(:key => 
 
201
  ['all_available_products', @latest_product.created_at].join(':'))
 
202
</ruby>
 
203
 
 
204
h4. Sweepers
 
205
 
 
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.
 
212
 
 
213
Continuing with our Product controller example, we could rewrite it with a
 
214
sweeper like this:
 
215
 
 
216
<ruby>
 
217
class StoreSweeper < ActionController::Caching::Sweeper
 
218
  # This sweeper is going to keep an eye on the Product model
 
219
  observe Product
 
220
 
 
221
  # If our sweeper detects that a Product was created call this
 
222
  def after_create(product)
 
223
          expire_cache_for(product)
 
224
  end
 
225
 
 
226
  # If our sweeper detects that a Product was updated call this
 
227
  def after_update(product)
 
228
          expire_cache_for(product)
 
229
  end
 
230
 
 
231
  # If our sweeper detects that a Product was deleted call this
 
232
  def after_destroy(product)
 
233
          expire_cache_for(product)
 
234
  end
 
235
 
 
236
  private
 
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')
 
240
 
 
241
    # Expire a fragment
 
242
    expire_fragment(:controller => '#{record}', 
 
243
      :action => 'recent', :action_suffix => 'all_products')
 
244
  end
 
245
end
 
246
</ruby>
 
247
 
 
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
 
250
following:
 
251
 
 
252
<ruby>
 
253
class ProductsController < ActionController
 
254
 
 
255
  before_filter :authenticate, :only => [ :edit, :create ]
 
256
  caches_page :list
 
257
  caches_action :edit
 
258
  cache_sweeper :store_sweeper, :only => [ :create ]
 
259
 
 
260
  def list; end
 
261
 
 
262
  def create
 
263
    expire_page :action => :list
 
264
    expire_action :action => :edit
 
265
  end
 
266
 
 
267
  def edit; end
 
268
 
 
269
end
 
270
</ruby>
 
271
 
 
272
h4. SQL Caching
 
273
 
 
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
 
277
database.
 
278
 
 
279
For example:
 
280
 
 
281
<ruby>
 
282
class ProductsController < ActionController
 
283
 
 
284
  before_filter :authenticate, :only => [ :edit, :create ]
 
285
  caches_page :list
 
286
  caches_action :edit
 
287
  cache_sweeper :store_sweeper, :only => [ :create ]
 
288
 
 
289
  def list
 
290
    # Run a find query
 
291
    Product.find(:all)
 
292
 
 
293
    ...
 
294
 
 
295
    # Run the same query again
 
296
    Product.find(:all)
 
297
  end
 
298
 
 
299
  def create
 
300
    expire_page :action => :list
 
301
    expire_action :action => :edit
 
302
  end
 
303
 
 
304
  def edit; end
 
305
 
 
306
end
 
307
</ruby>
 
308
 
 
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.
 
312
 
 
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.
 
315
 
 
316
h4. Cache Stores
 
317
 
 
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.
 
320
 
 
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
 
324
to do that.
 
325
 
 
326
The default cache stores provided with Rails include:
 
327
 
 
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.  
 
336
 
 
337
+MemoryStore+ is not only able to store strings, but also arbitrary Ruby objects.
 
338
 
 
339
+MemoryStore+ is not thread-safe. Use +SynchronizedMemoryStore+ instead if you
 
340
need thread-safety.
 
341
                                      
 
342
 
 
343
<ruby>
 
344
ActionController::Base.cache_store = :memory_store
 
345
</ruby>
 
346
 
 
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.
 
352
 
 
353
<ruby>
 
354
ActionController::Base.cache_store = :file_store, "/path/to/cache/directory"
 
355
</ruby>
 
356
 
 
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.
 
361
 
 
362
<ruby>
 
363
ActionController::Base.cache_store = :drb_store, "druby://localhost:9192"
 
364
</ruby>
 
365
 
 
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.
 
369
 
 
370
Special features:
 
371
 
 
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
 
375
   online.
 
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).
 
378
 
 
379
It also accepts a hash of additional options:
 
380
 
 
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.
 
384
 
 
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
 
387
marshaled
 
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.)
 
390
 
 
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). 
 
394
 
 
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.
 
399
 
 
400
 
 
401
<ruby>
 
402
ActionController::Base.cache_store = :mem_cache_store, "localhost"
 
403
</ruby>
 
404
 
 
405
5) ActiveSupport::Cache::SynchronizedMemoryStore: Like ActiveSupport::Cache::MemoryStore but thread-safe.
 
406
 
 
407
 
 
408
<ruby>
 
409
ActionController::Base.cache_store = :synchronized_memory_store
 
410
</ruby>
 
411
 
 
412
6) ActiveSupport::Cache::CompressedMemCacheStore: Works just like the regular
 
413
MemCacheStore but uses GZip to decompress/compress on read/write.
 
414
 
 
415
 
 
416
<ruby>
 
417
ActionController::Base.cache_store = :compressed_mem_cache_store, "localhost"
 
418
</ruby>
 
419
 
 
420
7) Custom store: You can define your own cache store (new in Rails 2.1)
 
421
 
 
422
 
 
423
<ruby>
 
424
ActionController::Base.cache_store = MyOwnStore.new("parameter")
 
425
</ruby>
 
426
 
 
427
NOTE: +config.cache_store+ can be used in place of
 
428
+ActionController::Base.cache_store+ in the +Rails::Initializer.run+ block in
 
429
environment.rb.
 
430
 
 
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
 
433
(if available).
 
434
 
 
435
An example:
 
436
 
 
437
<ruby>
 
438
Rails.cache.read("city")   # => nil
 
439
Rails.cache.write("city", "Duckburgh")
 
440
Rails.cache.read("city")   # => "Duckburgh"
 
441
</ruby>
 
442
 
 
443
h3. Conditional GET Support
 
444
 
 
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.
 
448
 
 
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
 
454
status.
 
455
 
 
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
 
459
easy task:
 
460
 
 
461
<ruby>
 
462
class ProductsController < ApplicationController
 
463
 
 
464
  def show
 
465
    @product = Product.find(params[:id])
 
466
 
 
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
 
472
      end
 
473
    end
 
474
 
 
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.
 
479
end
 
480
</ruby>
 
481
 
 
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:
 
485
 
 
486
<ruby>
 
487
class ProductsController < ApplicationController
 
488
 
 
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.
 
491
 
 
492
  def show
 
493
    @product = Product.find(params[:id])
 
494
    fresh_when :last_modified => @product.published_at.utc, :etag => @article
 
495
  end
 
496
end
 
497
</ruby>
 
498
 
 
499
h3. Advanced Caching
 
500
 
 
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.
 
507
 
 
508
Also the new "Cache money":http://github.com/nkallen/cache-money/tree/master plugin is supposed to be mad cool. 
 
509
 
 
510
h3. References
 
511
 
 
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
 
516
 
 
517
h3. Changelog
 
518
 
 
519
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/10-guide-to-caching
 
520
 
 
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