5 # A cache store implementation which stores data in Memcached:
6 # http://www.danga.com/memcached/
8 # This is currently the most popular cache store for production websites.
11 # - Clustering and load balancing. One can specify multiple memcached servers,
12 # and MemCacheStore will load balance between all available servers. If a
13 # server goes down, then MemCacheStore will ignore it until it goes back
15 # - Time-based expiry support. See #write and the +:expires_in+ option.
16 # - Per-request in memory cache for all communication with the MemCache server(s).
17 class MemCacheStore
< Store
18 module Response
# :nodoc:
20 NOT_STORED
= "NOT_STORED\r\n"
22 NOT_FOUND
= "NOT_FOUND\r\n"
23 DELETED
= "DELETED\r\n"
26 attr_reader
:addresses
28 # Creates a new MemCacheStore object, with the given memcached server
29 # addresses. Each address is either a host name, or a host-with-port string
30 # in the form of "host_name:port". For example:
32 # ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
34 # If no addresses are specified, then MemCacheStore will connect to
35 # localhost port 11211 (the default memcached port).
36 def initialize(*addresses
)
37 addresses
= addresses
.flatten
38 options
= addresses
.extract_options
!
39 addresses
= ["localhost"] if addresses
.empty
?
40 @addresses = addresses
41 @data = MemCache
.new(addresses
, options
)
43 extend Strategy
::LocalCache
46 def read(key
, options
= nil) # :nodoc:
48 @data.get(key
, raw
?(options
))
49 rescue MemCache
::MemCacheError => e
50 logger
.error("MemCacheError (#{e}): #{e.message}")
54 # Writes a value to the cache.
57 # - +:unless_exist+ - set to true if you don't want to update the cache
58 # if the key is already set.
59 # - +:expires_in+ - the number of seconds that this value may stay in
60 # the cache. See ActiveSupport::Cache::Store#write for an example.
61 def write(key
, value
, options
= nil)
63 method
= options
&& options
[:unless_exist] ? :add : :set
64 # memcache-client will break the connection if you send it an integer
65 # in raw mode, so we convert it to a string to be sure it continues working.
66 value
= value
.to_s
if raw
?(options
)
67 response
= @data.send(method
, key
, value
, expires_in(options
), raw
?(options
))
68 response
== Response
::STORED
69 rescue MemCache
::MemCacheError => e
70 logger
.error("MemCacheError (#{e}): #{e.message}")
74 def delete(key
, options
= nil) # :nodoc:
76 response
= @data.delete(key
, expires_in(options
))
77 response
== Response
::DELETED
78 rescue MemCache
::MemCacheError => e
79 logger
.error("MemCacheError (#{e}): #{e.message}")
83 def exist
?(key
, options
= nil) # :nodoc:
84 # Doesn't call super, cause exist? in memcache is in fact a read
85 # But who cares? Reading is very fast anyway
86 # Local cache is checked first, if it doesn't know then memcache itself is read from
87 !read(key
, options
).nil?
90 def increment(key
, amount
= 1) # :nodoc:
91 log("incrementing", key
, amount
)
93 response
= @data.incr(key
, amount
)
94 response
== Response
::NOT_FOUND ? nil : response
95 rescue MemCache
::MemCacheError
99 def decrement(key
, amount
= 1) # :nodoc:
100 log("decrement", key
, amount
)
101 response
= @data.decr(key
, amount
)
102 response
== Response
::NOT_FOUND ? nil : response
103 rescue MemCache
::MemCacheError
107 def delete_matched(matcher
, options
= nil) # :nodoc:
108 # don't do any local caching at present, just pass
109 # through and let the error happen
111 raise "Not supported by Memcache"
123 def expires_in(options
)
124 (options
&& options
[:expires_in]) || 0
128 options
&& options
[:raw]