From fead0fc44850480f2f431b8befb5cba284e5e633 Mon Sep 17 00:00:00 2001
From: Brian Durand <brian@embellishedvisions.com>
Date: Thu, 17 Feb 2011 10:12:28 -0600
Subject: update caching guide with the changes to ActiveSupport::Cache from
 the 3.0.0 release.

---
 railties/guides/source/caching_with_rails.textile | 94 +++++++++++++----------
 1 file changed, 52 insertions(+), 42 deletions(-)

(limited to 'railties/guides/source/caching_with_rails.textile')

diff --git a/railties/guides/source/caching_with_rails.textile b/railties/guides/source/caching_with_rails.textile
index 63c52da32a..1b5ec40d16 100644
--- a/railties/guides/source/caching_with_rails.textile
+++ b/railties/guides/source/caching_with_rails.textile
@@ -238,86 +238,95 @@ h3. Cache Stores
 
 Rails provides different stores for the cached data created by action and fragment caches. Page caches are always stored on disk.
 
-Rails 2.1 and above provide +ActiveSupport::Cache::Store+ which can be used to cache strings. Some cache store implementations, like +MemoryStore+, are able to cache arbitrary Ruby objects, but don't count on every cache store to be able to do that.
+h4. Configuration
 
-The default cache stores provided with Rails include:
-
-1) +ActiveSupport::Cache::MemoryStore+: A cache store implementation which stores everything into memory in the same process. If you're running multiple Ruby on Rails server processes (which is the case if you're using mongrel_cluster or Phusion Passenger), then this means that your Rails server process instances won't be able to share cache data with each other. If your application never performs manual cache item expiry (e.g. when you‘re using generational cache keys), then using +MemoryStore+ is ok. Otherwise, consider carefully whether you should be using this cache store.
-
-+MemoryStore+  is not only able to store strings, but also arbitrary Ruby objects.
-
-+MemoryStore+  is not thread-safe. Use +SynchronizedMemoryStore+ instead if you need thread-safety.
+You can set up your application's default cache store by calling +config.cache_store=+ in the Application definition inside your +config/application.rb+ file or in an Application.configure block in an environment specific configuration file (i.e. +config/environments/*.rb+). The first argument will be the cache store to use and the rest of the argument will be passed as arguments to the cache store constructor.
 
 <ruby>
-ActionController::Base.cache_store = :memory_store
+config.cache_store = :memory_store
 </ruby>
 
-2) +ActiveSupport::Cache::FileStore+: Cached data is stored on the disk, this is the default store and the default path for this store is +tmp/cache+. Works well for all types of environments and allows all processes running from the same application directory to access the cached content. If +tmp/cache+ does not exist, the default store becomes +MemoryStore+.
+Alternatively, you can call +ActionController::Base.cache_store+ outside of a configuration block.
 
-<ruby>
-ActionController::Base.cache_store = :file_store, "/path/to/cache/directory"
-</ruby>
+You can access the cache by calling +Rails.cache+.
 
-3) +ActiveSupport::Cache::DRbStore+: Cached data is stored in a separate shared DRb process that all servers communicate with. This works for all environments and only keeps one cache around for all processes, but requires that you run and manage a separate DRb process.
+h4. ActiveSupport::Cache::Store
 
-<ruby>
-ActionController::Base.cache_store = :drb_store, "druby://localhost:9192"
-</ruby>
+This class provides the foundation for interacting with the cache in Rails. This is an abstract class and you cannot use it on its own. Rather you must use a concrete implementation of the class tied to a storage engine. Rails ships with several implementations documented below.
+
+The main methods to call are +read+, +write+, +delete+, +exist?+, and +fetch+. The fetch method takes a block and will either return an existing value from the cache, or evaluate the block and write the result to the cache if no value exists.
 
-4) +ActiveSupport::Cache::MemCacheStore+: Works like +DRbStore+, but uses Danga's +memcached+ instead. Rails uses the bundled +memcached-client+ gem by default. This is currently the most popular cache store for production websites.
+There are some common options used by all cache implementations. These can be passed to the constructor or the various methods to interact with entries.
 
-Special features:
+* +:namespace+ - This option can be used to create a namespace within the cache store. It is especially useful if your application shares a cache with other applications. The default value will include the application name and Rails environment.
 
-* Clustering and load balancing. One can specify multiple memcached servers, and +MemCacheStore+ will load balance between all available servers. If a server goes down, then +MemCacheStore+ will ignore it until it goes back online.
-* Time-based expiry support. See +write+ and the +:expires_in+ option.
-* Per-request in memory cache for all communication with the +memcached+ server(s).
+* +:compress+ - This option can be used to indicate that compression should be used in the cache. This can be useful for transferring large cache entries over a slow network.
 
-It also accepts a hash of additional options:
+* +:compress_threshold+ - This options is used in conjunction with the +:compress+ option to indicate a threshold under which cache entries should not be compressed. This defaults to 16 kilobytes.
 
-* +:namespace+: specifies a string that will automatically be prepended to keys when accessing the memcached store.
-* +:readonly+: a boolean value that when set to true will make the store read-only, with an error raised on any attempt to write.
-* +: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.
+* +:expires_in+ - This option sets an expiration time in seconds for the cache entry when it will be automatically removed from the cache.
 
-The read and write methods of the +MemCacheStore+ accept an options hash too. When reading you can specify +:raw => true+ to prevent the object being marshaled (by default this is false which means the raw value in the cache is passed to +Marshal.load+ before being returned to you.)
+* +:race_condition_ttl+ - This option is used in conjunction with the +:expires_in+ option. It will prevent race conditions when cache entries expire by preventing multiple processes from simultaneously regenerating the same entry (also known as the dog pile effect). This option sets the number of seconds that an expired entry can be reused while a new value is being regenerated. It's a good practice to set this value if you use the +:expires_in+ option.
 
-When writing to the cache it is also possible to specify +:raw => true+ means the value is not passed to +Marshal.dump+ before being stored in the cache (by default this is false).
+h4. ActiveSupport::Cache::MemoryStore
 
-The write method also accepts an +:unless_exist+ flag which determines whether the memcached add (when true) or set (when false) method is used to store the item in the cache and an +:expires_in+ option that specifies the time-to-live for the cached item in seconds.
+This cache store keeps entries in memory in the same Ruby process. The cache store has a bounded size specified by the +:size+ options to the initializer (default is 32Mb). When the cache exceeds the allotted size, a cleanup will occur and the least recently used entries will be removed.
 
 <ruby>
-ActionController::Base.cache_store = :mem_cache_store, "localhost"
+ActionController::Base.cache_store = :memory_store, :size => 64.megabytes
 </ruby>
 
-5) +ActiveSupport::Cache::SynchronizedMemoryStore+: Like +MemoryStore+ but thread-safe.
+If you're running multiple Ruby on Rails server processes (which is the case if you're using mongrel_cluster or Phusion Passenger), then your Rails server process instances won't be able to share cache data with each other. This cache store is not appropriate for large application deployments, but can work well for small, low traffic sites with only a couple of server processes or for development and test environments.
+
+This is the default cache store implementation.
+
+h4. ActiveSupport::Cache::FileStore
+
+This cache store uses the file system to store entries. The path to the directory where the store files will be stored must be specified when initializing the cache.
 
 <ruby>
-ActionController::Base.cache_store = :synchronized_memory_store
+ActionController::Base.cache_store = :file_store, "/path/to/cache/directory"
 </ruby>
 
-6) +ActiveSupport::Cache::CompressedMemCacheStore+: Works just like the regular +MemCacheStore+ but uses GZip to decompress/compress on read/write.
+With this cache store, multiple server processes on the same host can share a cache. Servers processes running on different hosts could share a cache by using a shared file system, but that set up would not be ideal and is not recommended. The cache store is appropriate for low to medium traffic sites that are served off one or two hosts.
+
+Note that the cache will grow until the disk is full unless you periodically clear out old entries.
+
+h4. ActiveSupport::Cache::MemCacheStore
+
+This cache store uses Danga's +memcached+ server to provide a centralized cache for your application. Rails uses the bundled +memcached-client+ gem by default. This is currently the most popular cache store for production websites. It can be used to provide a single, shared cache cluster with very a high performance and redundancy.
+
+When initializing the cache, you need to specify the addresses for all memcached servers in your cluster. If none is specified, it will assume memcached is running on the local host on the default port, but this is not an ideal set up for larger sites.
+
+The +write+ and +fetch+ methods on this cache accept two additional options that take advantage of features specific to memcached. You can specify +:raw+ to send a value directly to the server with no serialization. The value must be a string or number. You can use memcached direct operation like +increment+ and +decrement+ only on raw values. You can also specify +:unless_exist+ if you don't want memcached to overwrite an existing entry.
 
 <ruby>
-ActionController::Base.cache_store = :compressed_mem_cache_store, "localhost"
+ActionController::Base.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.com"
 </ruby>
 
-7) Custom store: You can define your own cache store (new in Rails 2.1).
+h4. Custom Cache Stores
+
+You can create your own custom cache store by simply extending +ActiveSupport::Cache::Store+ and implementing the appropriate methods. In this way, you can swap in any number of caching technologies into your Rails application.
+
+To use a custom cache store, simple set the cache store to a new instance of the class.
 
 <ruby>
-ActionController::Base.cache_store = MyOwnStore.new("parameter")
+ActionController::Base.cache_store = MyCacheStore.new
 </ruby>
 
-NOTE: +config.cache_store+ can be used in place of +ActionController::Base.cache_store+ in your +Rails::Initializer.run+ block in +environment.rb+
+h4. Cache Keys
 
-In addition to all of this, Rails also adds the +ActiveRecord::Base#cache_key+ method that generates a key using the class name, +id+ and +updated_at+ timestamp (if available).
+The keys used in a cache can be any object that responds to either +:cache_key+ or to +:to_param+. You can implement the +:cache_key+ method on your classes if you need to generate custom keys. ActiveRecord will generate keys based on the class name and record id.
 
-You can access these cache stores at a low level for storing queries and other objects. Here's an example:
+You can use Hashes and Arrays of values as cache keys.
 
 <ruby>
-Rails.cache.read("city")   # => nil
-Rails.cache.write("city", "Duckburgh")
-Rails.cache.read("city")   # => "Duckburgh"
+# This is a legal cache key
+Rails.cache.read(:site => "mysite", :owners => [owner_1, owner2])
 </ruby>
 
+The keys you use on +Rails.cache+ will not be the same as those actually used with the storage engine. They may be modified with a namespace or altered to fit technology backend constraints. This means, for instance, that you can't save values with +Rails.cache+ and then try to pull them out with the +memcache-client+ gem. However, you also don't need to worry about exceeding the memcached size limit or violating syntax rules.
+
 h3. Conditional GET support
 
 Conditional GETs are a feature of the HTTP specification that provide a way for web servers to tell browsers that the response to a GET request hasn't changed since the last request and can be safely pulled from the browser cache.
@@ -369,6 +378,7 @@ h3. Further reading
 
 h3. Changelog
 
+* Feb       17, 2011: Document 3.0.0 changes to ActiveSupport::Cache
 * May       02, 2009: Formatting cleanups
 * April     26, 2009: Clean up typos in submitted patch
 * April      1, 2009: Made a bunch of small fixes
-- 
cgit v1.2.3

