1 require 'memcache'
2
3 module ActiveSupport
4 module Cache
5 # A cache store implementation which stores data in Memcached:
6 # http://www.danga.com/memcached/
7 #
8 # This is currently the most popular cache store for production websites.
9 #
10 # Special features:
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
14 # online.
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:
19 STORED = "STORED\r\n"
20 NOT_STORED = "NOT_STORED\r\n"
21 EXISTS = "EXISTS\r\n"
22 NOT_FOUND = "NOT_FOUND\r\n"
23 DELETED = "DELETED\r\n"
24 end
25
26 def self.build_mem_cache(*addresses)
27 addresses = addresses.flatten
28 options = addresses.extract_options!
29 addresses = ["localhost"] if addresses.empty?
30 MemCache.new(addresses, options)
31 end
32
33 # Creates a new MemCacheStore object, with the given memcached server
34 # addresses. Each address is either a host name, or a host-with-port string
35 # in the form of "host_name:port". For example:
36 #
37 # ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
38 #
39 # If no addresses are specified, then MemCacheStore will connect to
40 # localhost port 11211 (the default memcached port).
41 #
42 # Instead of addresses one can pass in a MemCache-like object. For example:
43 #
44 # require 'memcached' # gem install memcached; uses C bindings to libmemcached
45 # ActiveSupport::Cache::MemCacheStore.new(Memcached::Rails.new("localhost:11211"))
46 def initialize(*addresses)
47 if addresses.first.respond_to?(:get)
48 @data = addresses.first
49 else
50 @data = self.class.build_mem_cache(*addresses)
51 end
52
53 extend Strategy::LocalCache
54 end
55
56 # Reads multiple keys from the cache.
57 def read_multi(*keys)
58 @data.get_multi keys
59 end
60
61 def read(key, options = nil) # :nodoc:
62 super
63 @data.get(key, raw?(options))
64 rescue MemCache::MemCacheError => e
65 logger.error("MemCacheError (#{e}): #{e.message}")
66 nil
67 end
68
69 # Writes a value to the cache.
70 #
71 # Possible options:
72 # - +:unless_exist+ - set to true if you don't want to update the cache
73 # if the key is already set.
74 # - +:expires_in+ - the number of seconds that this value may stay in
75 # the cache. See ActiveSupport::Cache::Store#write for an example.
76 def write(key, value, options = nil)
77 super
78 method = options && options[:unless_exist] ? :add : :set
79 # memcache-client will break the connection if you send it an integer
80 # in raw mode, so we convert it to a string to be sure it continues working.
81 value = value.to_s if raw?(options)
82 response = @data.send(method, key, value, expires_in(options), raw?(options))
83 response == Response::STORED
84 rescue MemCache::MemCacheError => e
85 logger.error("MemCacheError (#{e}): #{e.message}")
86 false
87 end
88
89 def delete(key, options = nil) # :nodoc:
90 super
91 response = @data.delete(key, expires_in(options))
92 response == Response::DELETED
93 rescue MemCache::MemCacheError => e
94 logger.error("MemCacheError (#{e}): #{e.message}")
95 false
96 end
97
98 def exist?(key, options = nil) # :nodoc:
99 # Doesn't call super, cause exist? in memcache is in fact a read
100 # But who cares? Reading is very fast anyway
101 # Local cache is checked first, if it doesn't know then memcache itself is read from
102 !read(key, options).nil?
103 end
104
105 def increment(key, amount = 1) # :nodoc:
106 log("incrementing", key, amount)
107
108 response = @data.incr(key, amount)
109 response == Response::NOT_FOUND ? nil : response
110 rescue MemCache::MemCacheError
111 nil
112 end
113
114 def decrement(key, amount = 1) # :nodoc:
115 log("decrement", key, amount)
116 response = @data.decr(key, amount)
117 response == Response::NOT_FOUND ? nil : response
118 rescue MemCache::MemCacheError
119 nil
120 end
121
122 def delete_matched(matcher, options = nil) # :nodoc:
123 # don't do any local caching at present, just pass
124 # through and let the error happen
125 super
126 raise "Not supported by Memcache"
127 end
128
129 def clear
130 @data.flush_all
131 end
132
133 def stats
134 @data.stats
135 end
136
137 private
138 def raw?(options)
139 options && options[:raw]
140 end
141 end
142 end
143 end