libmemcached_store 0.2.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fd3263466b00f991543c49eae9ed95cf60808d48
4
+ data.tar.gz: 3f8780d95967e725cc6b85a2d11236616ee34baa
5
+ SHA512:
6
+ metadata.gz: 60147888a5c9aedc996e3597dc99deba43fac634c6e57febba5447208da63147fdd5387ede6cff4c3f6e6d5edccd25a137b6fe1840b6ca692e70c1cb68764a0a
7
+ data.tar.gz: 8be98b2735c5c6f9a0d004463622523defce2ca0e76263aae6f616fffc19dfedf7ffd3eedf026796bf46455b17843de910efd5ee4702d9b8c94d930dbffdd2bc
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .DS_Store
2
+ *.swp
3
+ Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ gemfile:
5
+ - gemfiles/rails30.gemfile
6
+ - gemfiles/rails31.gemfile
7
+ - gemfiles/rails32.gemfile
data/BENCHMARKS ADDED
@@ -0,0 +1,37 @@
1
+ Testing with
2
+ ruby 1.9.3p286 (2012-10-12 revision 37165) [x86_64-darwin12.2.0]
3
+ Dalli 2.6.3
4
+ Libmemcached_store 0.6.0
5
+ user system total real
6
+ write:short:dalli 0.380000 0.040000 0.420000 ( 0.422342)
7
+ write:short:libm 0.040000 0.020000 0.060000 ( 0.125838)
8
+ write:long:dalli 0.370000 0.040000 0.410000 ( 0.407400)
9
+ write:long:libm 0.050000 0.030000 0.080000 ( 0.130482)
10
+ write:raw:dalli 0.330000 0.030000 0.360000 ( 0.371981)
11
+ write:raw:libm 0.050000 0.030000 0.080000 ( 0.122899)
12
+
13
+ read:miss:dalli 0.310000 0.040000 0.350000 ( 0.352729)
14
+ read:miss:libm 0.080000 0.040000 0.120000 ( 0.182143)
15
+ read:miss2:dalli 0.350000 0.040000 0.390000 ( 0.394071)
16
+ read:miss2:libm 0.090000 0.050000 0.140000 ( 0.190611)
17
+ read:exist:dalli 0.350000 0.040000 0.390000 ( 0.391384)
18
+ read:exist:libm 0.060000 0.040000 0.100000 ( 0.161597)
19
+ read:expired:dalli 0.350000 0.040000 0.390000 ( 0.399342)
20
+ read:expired:libm 0.080000 0.050000 0.130000 ( 0.175674)
21
+ read:raw:dalli 0.360000 0.040000 0.400000 ( 0.400142)
22
+ read:raw:libm 0.050000 0.040000 0.090000 ( 0.160905)
23
+
24
+ exist:miss:dalli 0.310000 0.040000 0.350000 ( 0.342454)
25
+ exist:miss:libm 0.040000 0.030000 0.070000 ( 0.141901)
26
+ exist:hit:dalli 0.320000 0.040000 0.360000 ( 0.354205)
27
+ exist:hit:libm 0.030000 0.020000 0.050000 ( 0.107114)
28
+
29
+ delete:miss:dalli 0.310000 0.040000 0.350000 ( 0.342343)
30
+ delete:miss:libm 0.060000 0.030000 0.090000 ( 0.141960)
31
+ delete:hit:dalli 0.350000 0.040000 0.390000 ( 0.389187)
32
+ delete:hit:libm 0.050000 0.020000 0.070000 ( 0.130604)
33
+
34
+ increment:dalli 0.380000 0.050000 0.430000 ( 0.422802)
35
+ increment:libm 0.040000 0.020000 0.060000 ( 0.119200)
36
+ decrement:dalli 0.390000 0.040000 0.430000 ( 0.426890)
37
+ decrement:libm 0.030000 0.030000 0.060000 ( 0.118954)
data/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ ## 0.6.0
4
+ * New gem name _libmemcached_store_
5
+ * Handle Memcached::Error in read_multi (staugaard)
6
+
7
+ ## 0.5.1
8
+ * Remove warning from latest version of mocha
9
+ * Make #clear compatible with Rails.cache#clear (grosser)
10
+
11
+ ## 0.5.0
12
+ * Use Memcached#exist if available (performance improvement ~25%)
13
+ * Correctly escape bad characters and too long keys
14
+ * Add benchmarks
15
+ * Remove the use of ActiveSupport::Entry which was a performance bottleneck #3
16
+
17
+ ## 0.4.0
18
+ * Optimize read_multi to only make one call to memecached server
19
+ * Update test suite to reflect Rails' one
20
+ * Add session store tests
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem 'dalli'
7
+ end
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 37signals
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # LibmemcachedStore
2
+
3
+ An ActiveSupport cache store that uses the C-based libmemcached client through Evan Weaver's Ruby/SWIG wrapper, [memcached](https://github.com/evan/memcached). libmemcached is fast (fastest memcache client for Ruby), lightweight, and supports consistent hashing, non-blocking IO, and graceful server failover.
4
+
5
+ This cache is designed for Rails 3+ applications.
6
+
7
+ ## Prerequisites
8
+
9
+ You'll need the memcached gem installed:
10
+
11
+ ```ruby
12
+ gem install memcached
13
+ ```
14
+
15
+ or in your Gemfile
16
+
17
+ ```ruby
18
+ gem 'memcached'
19
+ ```
20
+
21
+ There are no other dependencies.
22
+
23
+ ## Installation
24
+
25
+ Just add to your Gemfile
26
+
27
+ ```ruby
28
+ gem 'libmemcached_store', '~> 0.6.0'
29
+ ```
30
+
31
+ and you're set.
32
+
33
+ ## Usage
34
+
35
+ This is a drop-in replacement for the memcache store that ships with Rails. To
36
+ enable, set the `config.cache_store` option to `libmemcached_store`
37
+ in the config for your environment
38
+
39
+ ```ruby
40
+ config.cache_store = :libmemcached_store
41
+ ```
42
+
43
+ If no servers are specified, localhost is assumed. You can specify a list of
44
+ server addresses, either as hostnames or IP addresses, with or without a port
45
+ designation. If no port is given, 11211 is assumed:
46
+
47
+ ```ruby
48
+ config.cache_store = :libmemcached_store, %w(cache-01 cache-02 127.0.0.1:11212)
49
+ ```
50
+
51
+ Standard Rails cache store options can be used
52
+
53
+ ```ruby
54
+ config.cache_store = :libmemcached_store, '127.0.0.1:11211', {:compress => true, :expires_in => 3600}
55
+ ```
56
+
57
+ More advanced options can be passed directly to the client
58
+
59
+ ```ruby
60
+ config.cache_store = :libmemcached_store, '127.0.0.1:11211', {:client => { :binary_protocol => true, :no_block => true }}
61
+ ```
62
+
63
+ You can also use `:libmemcached_store` to store your application sessions
64
+
65
+ ```ruby
66
+ require 'action_dispatch/session/libmemcached_store'
67
+ config.session_store = :libmemcached_store, :namespace => '_session', :expire_after => 1800
68
+ ```
69
+
70
+ ## Performance
71
+
72
+ Used with Rails, __libmemcached_store__ is at least 1.5x faster than __dalli__. See [BENCHMARKS](https://github.com/ccocchi/libmemcached_store/blob/master/BENCHMARKS)
73
+ for details
74
+
75
+ ## Props
76
+
77
+ Thanks to Brian Aker ([http://tangent.org](http://tangent.org)) for creating libmemcached, and Evan
78
+ Weaver ([http://blog.evanweaver.com](http://blog.evanweaver.com)) for the Ruby wrapper.
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'rake'
5
+ require 'rake/testtask'
6
+ require 'rdoc/task'
7
+
8
+ task :default => :test
9
+
10
+ Rake::TestTask.new do |t|
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.warning = false
14
+ t.verbose = true
15
+ end
16
+
17
+ desc 'Generate documentation for the libmemcached_store plugin.'
18
+ Rake::RDocTask.new(:rdoc) do |rdoc|
19
+ rdoc.rdoc_dir = 'rdoc'
20
+ rdoc.title = 'LibmemcachedStore'
21
+ rdoc.options << '--line-numbers' << '--inline-source'
22
+ rdoc.rdoc_files.include('README')
23
+ rdoc.rdoc_files.include('lib/**/*.rb')
24
+ end
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'activesupport', '~> 3.0.0'
4
+ gem 'actionpack', '~> 3.0.0'
5
+ gem 'mocha'
6
+
7
+ gemspec :path => '../'
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'activesupport', '~> 3.1.0'
4
+ gem 'actionpack', '~> 3.1.0'
5
+ gem 'mocha'
6
+
7
+ gemspec :path => '../'
@@ -0,0 +1,7 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'activesupport', '~> 3.2.0'
4
+ gem 'actionpack', '~> 3.2.0'
5
+ gem 'mocha'
6
+
7
+ gemspec :path => '../'
@@ -0,0 +1,80 @@
1
+ require 'memcached'
2
+ require 'action_dispatch/middleware/session/abstract_store'
3
+
4
+ module ActionDispatch
5
+ module Session
6
+ class LibmemcachedStore < AbstractStore
7
+
8
+ def initialize(app, options = {})
9
+ options[:expire_after] ||= options[:expires]
10
+ super
11
+ client_options = { default_ttl: options.fetch(:expire_after, 0) }
12
+ client_options[:namespace] = options[:namespace] || 'rack:session'
13
+ @mutex = Mutex.new
14
+ @pool = options[:cache] || Memcached.new(@default_options[:memcache_server], client_options)
15
+ end
16
+
17
+ private
18
+
19
+ def generate_sid
20
+ loop do
21
+ sid = super
22
+ begin
23
+ @pool.exist(sid)
24
+ rescue Memcached::NotFound
25
+ break sid
26
+ end
27
+ end
28
+ end
29
+
30
+ def get_session(env, sid)
31
+ sid ||= generate_sid
32
+ session = with_lock(env, {}) do
33
+ begin
34
+ @pool.get(sid)
35
+ rescue Memcached::NotFound
36
+ {}
37
+ end
38
+ end
39
+ [sid, session]
40
+ end
41
+
42
+ def set_session(env, session_id, new_session, options = {})
43
+ expiry = options[:expire_after].to_i
44
+
45
+ with_lock(env, false) do
46
+ @pool.set(session_id, new_session, expiry)
47
+ session_id
48
+ end
49
+ end
50
+
51
+ def destroy_session(env, session_id, options = {})
52
+ with_lock(env, nil) do
53
+ @pool.delete(session_id)
54
+ generate_sid unless options[:drop]
55
+ end
56
+ end
57
+
58
+ #
59
+ # Deprecated since Rails 3.1.0
60
+ #
61
+ def destroy(env)
62
+ if sid = current_session_id(env)
63
+ with_lock(env, false) do
64
+ @pool.delete(sid)
65
+ end
66
+ end
67
+ end
68
+
69
+ def with_lock(env, default)
70
+ @mutex.lock if env['rack.multithread']
71
+ yield
72
+ rescue Memcached::Error => e
73
+ default
74
+ ensure
75
+ @mutex.unlock if @mutex.locked?
76
+ end
77
+
78
+ end
79
+ end
80
+ end
@@ -1,53 +1,138 @@
1
1
  require 'memcached'
2
- require 'digest/sha1'
3
-
4
- class Memcached
5
- # The latest version of memcached (0.11) doesn't support hostnames with dashes
6
- # in their names, so we overwrite it here to be more lenient.
7
- def set_servers(servers)
8
- [*servers].each_with_index do |server, index|
9
- host, port = server.split(":")
10
- Lib.memcached_server_add(@struct, host, port.to_i)
11
- end
12
- end
13
- end
2
+ require 'memcached/get_with_flags'
3
+
4
+ require 'digest/md5'
14
5
 
15
6
  module ActiveSupport
16
7
  module Cache
17
- class LibmemcachedStore < Store
8
+
9
+ #
10
+ # Store using memcached gem as client
11
+ #
12
+ # Global options can be passed to be applied to each method by default.
13
+ # Supported options are
14
+ # * <tt>:compress</tt> : if set to true, data will be compress before stored
15
+ # * <tt>:compress_threshold</tt> : specify the threshold at which to compress
16
+ # value, default is 4K
17
+ # * <tt>:namespace</tt> : prepend each key with this value for simple namespacing
18
+ # * <tt>:expires_in</tt> : default TTL in seconds for each. Default value is 0, i.e. forever
19
+ # Specific value can be passed per key with write and fetch command.
20
+ #
21
+ # Options can also be passed direclty to the memcache client, via the <tt>:client</tt>
22
+ # option. For example, if you want to use pipelining, you can use
23
+ # :client => { :no_block => true }
24
+ #
25
+ class LibmemcachedStore
18
26
  attr_reader :addresses
19
27
 
20
- DEFAULT_OPTIONS = {
21
- :distribution => :consistent,
22
- :no_block => true,
23
- :failover => true
24
- }
28
+ DEFAULT_CLIENT_OPTIONS = { distribution: :consistent_ketama, binary_protocol: true, default_ttl: 0 }
29
+ ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
30
+ DEFAULT_COMPRESS_THRESHOLD = 4096
31
+ FLAG_COMPRESSED = 0x2
32
+
33
+ attr_reader :silence, :options
34
+ alias_method :silence?, :silence
35
+
36
+ # Silence the logger.
37
+ def silence!
38
+ @silence = true
39
+ self
40
+ end
41
+
42
+ # Silence the logger within a block.
43
+ def mute
44
+ previous_silence, @silence = defined?(@silence) && @silence, true
45
+ yield
46
+ ensure
47
+ @silence = previous_silence
48
+ end
25
49
 
26
50
  def initialize(*addresses)
27
51
  addresses.flatten!
28
52
  options = addresses.extract_options!
29
- addresses = %w(localhost) if addresses.empty?
53
+ client_options = options.delete(:client) || {}
54
+ if options[:namespace]
55
+ client_options[:prefix_key] = options.delete(:namespace)
56
+ client_options[:prefix_delimiter] = ':'
57
+ @namespace_length = client_options[:prefix_key].length + 1
58
+ else
59
+ @namespace_length = 0
60
+ end
61
+ client_options[:default_ttl] = options.delete(:expires_in).to_i if options[:expires_in]
30
62
 
63
+ @options = options.reverse_merge(compress_threshold: DEFAULT_COMPRESS_THRESHOLD)
31
64
  @addresses = addresses
32
- @cache = Memcached.new(@addresses, options.reverse_merge(DEFAULT_OPTIONS))
33
- extend ActiveSupport::Cache::Strategy::LocalCache
65
+ @cache = Memcached.new(@addresses, client_options.reverse_merge(DEFAULT_CLIENT_OPTIONS))
66
+ @cache.instance_eval { send(:extend, GetWithFlags) }
34
67
  end
35
68
 
36
- def valid_key(key)
37
- if key.is_a?(Array)
38
- key.map {|k| valid_key(k) }
39
- else
40
- if key && key.size > 250
41
- "#{Digest::SHA1.hexdigest(key)}-autofixed"
69
+ def fetch(key, options = nil)
70
+ if block_given?
71
+ key = expanded_key(key)
72
+ unless options && options[:force]
73
+ entry = instrument(:read, key, options) do |payload|
74
+ payload[:super_operation] = :fetch if payload
75
+ read_entry(key, options)
76
+ end
77
+ end
78
+
79
+ if entry.nil?
80
+ result = instrument(:generate, key, options) do |payload|
81
+ yield
82
+ end
83
+ write_entry(key, result, options)
84
+ result
42
85
  else
43
- key
86
+ instrument(:fetch_hit, key, options) { |payload| }
87
+ entry
44
88
  end
89
+ else
90
+ read(key, options)
45
91
  end
46
92
  end
47
93
 
48
94
  def read(key, options = nil)
49
- super
50
- @cache.get(valid_key(key), marshal?(options))
95
+ key = expanded_key(key)
96
+ instrument(:read, key, options) do |payload|
97
+ entry = read_entry(key, options)
98
+ payload[:hit] = !!entry if payload
99
+ entry
100
+ end
101
+ end
102
+
103
+ def write(key, value, options = nil)
104
+ key = expanded_key(key)
105
+ instrument(:write, key, options) do |payload|
106
+ write_entry(key, value, options)
107
+ end
108
+ end
109
+
110
+ def delete(key, options = nil)
111
+ key = expanded_key(key)
112
+ instrument(:delete, key) do |payload|
113
+ delete_entry(key, options)
114
+ end
115
+ end
116
+
117
+ def exist?(key, options = nil)
118
+ key = expanded_key(key)
119
+ instrument(:exist?, key) do |payload|
120
+ if @cache.respond_to?(:exist)
121
+ @cache.exist(escape_and_normalize(key))
122
+ true
123
+ else
124
+ read_entry(key, options) != nil
125
+ end
126
+ end
127
+ rescue Memcached::NotFound
128
+ false
129
+ end
130
+
131
+ def increment(key, amount = 1, options = nil)
132
+ key = expanded_key(key)
133
+ instrument(:increment, key, amount: amount) do
134
+ @cache.incr(escape_and_normalize(key), amount)
135
+ end
51
136
  rescue Memcached::NotFound
52
137
  nil
53
138
  rescue Memcached::Error => e
@@ -55,86 +140,147 @@ module ActiveSupport
55
140
  nil
56
141
  end
57
142
 
58
- def read_multi(*keys)
59
- read(keys) || {}
143
+ def decrement(key, amount = 1, options = nil)
144
+ key = expanded_key(key)
145
+ instrument(:decrement, key, amount: amount) do
146
+ @cache.decr(escape_and_normalize(key), amount)
147
+ end
148
+ rescue Memcached::NotFound
149
+ nil
150
+ rescue Memcached::Error => e
151
+ log_error(e)
152
+ nil
60
153
  end
61
154
 
62
- # Set the key to the given value. Pass :unless_exist => true if you want to
63
- # skip setting a key that already exists.
64
- def write(key, value, options = nil)
65
- super
66
- method = (options && options[:unless_exist]) ? :add : :set
67
- @cache.send(method, valid_key(key), value, expires_in(options), marshal?(options))
155
+ def read_multi(*names)
156
+ names.flatten!
157
+ options = names.extract_options!
158
+
159
+ return {} if names.empty?
160
+
161
+ mapping = Hash[names.map {|name| [escape_and_normalize(expanded_key(name)), name] }]
162
+ raw_values, flags = @cache.get(mapping.keys, false, true)
163
+
164
+ values = {}
165
+ raw_values.each do |key, value|
166
+ values[mapping[key]] = deserialize(value, options[:raw], flags[key])
167
+ end
168
+ values
169
+ rescue Memcached::Error => e
170
+ log_error(e)
171
+ {}
172
+ end
173
+
174
+ def clear(options = nil)
175
+ @cache.flush
176
+ end
177
+
178
+ def stats
179
+ @cache.stats
180
+ end
181
+
182
+ protected
183
+
184
+ def read_entry(key, options = nil)
185
+ options ||= {}
186
+ raw_value, flags = @cache.get(escape_and_normalize(key), false, true)
187
+ deserialize(raw_value, options[:raw], flags)
188
+ rescue Memcached::NotFound
189
+ nil
190
+ rescue Memcached::Error => e
191
+ log_error(e)
192
+ nil
193
+ end
194
+
195
+ def write_entry(key, entry, options = nil)
196
+ options = options ? @options.merge(options) : @options
197
+ method = options[:unless_exist] ? :add : :set
198
+ entry = options[:raw] ? entry.to_s : Marshal.dump(entry)
199
+ flags = 0
200
+
201
+ if options[:compress] && entry.bytesize >= options[:compress_threshold]
202
+ entry = Zlib::Deflate.deflate(entry)
203
+ flags |= FLAG_COMPRESSED
204
+ end
205
+
206
+ @cache.send(method, escape_and_normalize(key), entry, options[:expires_in].to_i, false, flags)
68
207
  true
69
208
  rescue Memcached::Error => e
70
209
  log_error(e)
71
210
  false
72
211
  end
73
212
 
74
- def delete(key, options = nil)
75
- super
76
- @cache.delete(valid_key(key))
213
+ def delete_entry(key, options = nil)
214
+ @cache.delete(escape_and_normalize(key))
77
215
  true
78
216
  rescue Memcached::NotFound
79
- nil
217
+ false
80
218
  rescue Memcached::Error => e
81
219
  log_error(e)
82
220
  false
83
221
  end
84
222
 
85
- def exist?(key, options = nil)
86
- !read(key, options).nil?
87
- end
223
+ private
88
224
 
89
- def increment(key, amount=1)
90
- log 'incrementing', key, amount
91
- @cache.incr(valid_key(key), amount)
92
- rescue Memcached::Error
93
- nil
225
+ def deserialize(value, raw = false, flags = 0)
226
+ value = Zlib::Inflate.inflate(value) if (flags & FLAG_COMPRESSED) != 0
227
+ raw ? value : Marshal.load(value)
228
+ rescue TypeError, ArgumentError
229
+ value
94
230
  end
95
231
 
96
- def decrement(key, amount=1)
97
- log 'decrementing', key, amount
98
- @cache.decr(valid_key(key), amount)
99
- rescue Memcached::Error
100
- nil
101
- end
232
+ def escape_and_normalize(key)
233
+ key = key.to_s.force_encoding("BINARY").gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
234
+ key_length = key.length
102
235
 
103
- def delete_matched(matcher, options = nil)
104
- super
105
- raise NotImplementedError
106
- end
236
+ return key if @namespace_length + key_length <= 250
107
237
 
108
- # Flushes all data in memory
109
- def clear
110
- @cache.flush
238
+ max_key_length = 213 - @namespace_length
239
+ "#{key[0, max_key_length]}:md5:#{Digest::MD5.hexdigest(key)}"
111
240
  end
112
241
 
113
- def stats
114
- @cache.stats
115
- end
242
+ def expanded_key(key) # :nodoc:
243
+ return key.cache_key.to_s if key.respond_to?(:cache_key)
244
+
245
+ case key
246
+ when Array
247
+ if key.size > 1
248
+ key = key.collect { |element| expanded_key(element) }
249
+ else
250
+ key = key.first
251
+ end
252
+ when Hash
253
+ key = key.sort_by { |k,_| k.to_s }.collect { |k, v| "#{k}=#{v}" }
254
+ end
116
255
 
117
- # Resets server connections, forcing a reconnect. This is required in
118
- # cases where processes fork, but continue sharing the same memcached
119
- # connection. You want to call this after the fork to make sure the
120
- # new process has its own connection.
121
- def reset
122
- @cache.reset
256
+ key.to_param
123
257
  end
124
258
 
125
- private
259
+ def instrument(operation, key, options=nil)
260
+ log(operation, key, options)
126
261
 
127
- def expires_in(options)
128
- (options || {})[:expires_in] || 0
262
+ if ActiveSupport::Cache::Store.instrument
263
+ payload = { :key => key }
264
+ payload.merge!(options) if options.is_a?(Hash)
265
+ ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
266
+ else
267
+ yield(nil)
129
268
  end
269
+ end
130
270
 
131
- def marshal?(options)
132
- !(options || {})[:raw]
133
- end
271
+ def log(operation, key, options=nil)
272
+ return unless !silence? && logger && logger.debug?
273
+ logger.debug("Cache #{operation}: #{key}#{options.blank? ? "" : " (#{options.inspect})"}")
274
+ end
134
275
 
135
- def log_error(exception)
136
- logger.error "MemcachedError (#{exception.inspect}): #{exception.message}" if logger && !@logger_off
137
- end
276
+ def log_error(exception)
277
+ return unless !silence? && logger && logger.error?
278
+ logger.error "MemcachedError (#{exception.inspect}): #{exception.message}"
279
+ end
280
+
281
+ def logger
282
+ Rails.logger
283
+ end
138
284
  end
139
285
  end
140
286
  end