memcached_store 0.12.8 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE +2 -2
- data/README.md +6 -2
- data/lib/active_support/cache/memcached_snappy_store.rb +6 -7
- data/lib/active_support/cache/memcached_store.rb +110 -78
- data/lib/memcached_store.rb +1 -1
- data/lib/memcached_store/version.rb +1 -1
- metadata +8 -22
- data/.gitignore +0 -20
- data/.travis.yml +0 -16
- data/Gemfile +0 -21
- data/Rakefile +0 -21
- data/lib/memcached_store/memcached_safety.rb +0 -88
- data/memcached_store.gemspec +0 -22
- data/shipit.rubygems.yml +0 -4
- data/test/test_helper.rb +0 -21
- data/test/test_memcached_safety.rb +0 -142
- data/test/test_memcached_snappy_store.rb +0 -172
- data/test/test_memcached_store.rb +0 -601
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0915f7c2305d9b4962115d99c52997673e711a58
|
4
|
+
data.tar.gz: 0cc5bbca84c1aee1bcf70f782735d90cd562252d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8a0873627bf43775afd6ef3a7aeb9788a3b5d96d82eaac63f3a49879942e43daf9ace73d8c90c697fc0ca4a4f8a6ee777b3314339634c18734953c84e065d94
|
7
|
+
data.tar.gz: 5326f2375fa65be54b8c0f5a9c163e0a606c8f6a474ee21f7476c8175fa39252813392e206ad599494b3b72251b6054ad8c824024e963dd9addc3ea16316513c
|
data/LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2013
|
1
|
+
Copyright (c) 2013-2016 Shopify Inc.
|
2
2
|
|
3
3
|
MIT License
|
4
4
|
|
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
19
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
20
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
21
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -8,7 +8,7 @@ ActiveSupport memcached store. This wraps the memcached gem into a ActiveSupport
|
|
8
8
|
|
9
9
|
### MemcachedSnappyStore
|
10
10
|
|
11
|
-
ActiveSupport cache store that adds snappy compression at the cost of making the
|
11
|
+
ActiveSupport cache store that adds snappy compression at the cost of making the `incr, decr` operations unavailable.
|
12
12
|
|
13
13
|
## Installation
|
14
14
|
|
@@ -40,9 +40,13 @@ config.cache_store = :memcached_store,
|
|
40
40
|
|
41
41
|
# for snappy store
|
42
42
|
config.cache_store = :memcached_snappy_store,
|
43
|
-
Memcached::Rails.new(:
|
43
|
+
Memcached::Rails.new(servers: ['memcached1.foo.com', 'memcached2.foo.com'])
|
44
44
|
```
|
45
45
|
|
46
|
+
## Benchmarks
|
47
|
+
|
48
|
+
For benchmarks please refer to https://github.com/basecamp/memcached_bench.
|
49
|
+
|
46
50
|
## Code status
|
47
51
|
|
48
52
|
[](https://travis-ci.org/Shopify/memcached_store)
|
@@ -12,12 +12,12 @@ module ActiveSupport
|
|
12
12
|
class MemcachedSnappyStore < MemcachedStore
|
13
13
|
class UnsupportedOperation < StandardError; end
|
14
14
|
|
15
|
-
def increment(*
|
16
|
-
raise UnsupportedOperation
|
15
|
+
def increment(*)
|
16
|
+
raise UnsupportedOperation, "increment is not supported by: #{self.class.name}"
|
17
17
|
end
|
18
18
|
|
19
|
-
def decrement(*
|
20
|
-
raise UnsupportedOperation
|
19
|
+
def decrement(*)
|
20
|
+
raise UnsupportedOperation, "decrement is not supported by: #{self.class.name}"
|
21
21
|
end
|
22
22
|
|
23
23
|
# IdentityCache has its own handling for read only.
|
@@ -26,6 +26,7 @@ module ActiveSupport
|
|
26
26
|
end
|
27
27
|
|
28
28
|
private
|
29
|
+
|
29
30
|
def serialize_entry(entry, options)
|
30
31
|
value = options[:raw] ? entry.value.to_s : Marshal.dump(entry)
|
31
32
|
[Snappy.deflate(value), true]
|
@@ -34,12 +35,10 @@ module ActiveSupport
|
|
34
35
|
def deserialize_entry(compressed_value)
|
35
36
|
if compressed_value
|
36
37
|
super(Snappy.inflate(compressed_value))
|
37
|
-
else
|
38
|
-
nil
|
39
38
|
end
|
40
39
|
end
|
41
40
|
|
42
|
-
def cas_raw?(
|
41
|
+
def cas_raw?(_options)
|
43
42
|
true
|
44
43
|
end
|
45
44
|
end
|
@@ -11,21 +11,20 @@ module ActiveSupport
|
|
11
11
|
# MemcachedStore implements the Strategy::LocalCache strategy which implements
|
12
12
|
# an in-memory cache inside of a block.
|
13
13
|
class MemcachedStore < Store
|
14
|
-
FATAL_EXCEPTIONS = [
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
NONFATAL_EXCEPTIONS = []
|
14
|
+
FATAL_EXCEPTIONS = [Memcached::ABadKeyWasProvidedOrCharactersOutOfRange,
|
15
|
+
Memcached::AKeyLengthOfZeroWasProvided,
|
16
|
+
Memcached::ConnectionBindFailure,
|
17
|
+
Memcached::ConnectionDataDoesNotExist,
|
18
|
+
Memcached::ConnectionFailure,
|
19
|
+
Memcached::ConnectionSocketCreateFailure,
|
20
|
+
Memcached::CouldNotOpenUnixSocket,
|
21
|
+
Memcached::NoServersDefined,
|
22
|
+
Memcached::TheHostTransportProtocolDoesNotMatchThatOfTheClient]
|
23
|
+
|
24
|
+
NONFATAL_EXCEPTIONS = if defined?(::Rails) && ::Rails.env.test?
|
25
|
+
[]
|
27
26
|
else
|
28
|
-
|
27
|
+
Memcached::EXCEPTIONS - FATAL_EXCEPTIONS
|
29
28
|
end
|
30
29
|
|
31
30
|
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
|
@@ -41,7 +40,7 @@ module ActiveSupport
|
|
41
40
|
@data = addresses.first
|
42
41
|
else
|
43
42
|
mem_cache_options = options.dup
|
44
|
-
UNIVERSAL_OPTIONS.each{|name| mem_cache_options.delete(name)}
|
43
|
+
UNIVERSAL_OPTIONS.each { |name| mem_cache_options.delete(name) }
|
45
44
|
@data = Memcached::Rails.new(*(addresses + [mem_cache_options]))
|
46
45
|
end
|
47
46
|
|
@@ -53,24 +52,24 @@ module ActiveSupport
|
|
53
52
|
@logger = ::Rails.logger if defined?(::Rails)
|
54
53
|
end
|
55
54
|
|
56
|
-
def write(*
|
55
|
+
def write(*)
|
57
56
|
return true if read_only
|
58
|
-
super
|
57
|
+
super
|
59
58
|
end
|
60
59
|
|
61
|
-
def delete(*
|
60
|
+
def delete(*)
|
62
61
|
return true if read_only
|
63
|
-
super
|
62
|
+
super
|
64
63
|
end
|
65
64
|
|
66
65
|
def read_multi(*names)
|
67
66
|
options = names.extract_options!
|
68
67
|
options = merged_options(options)
|
69
|
-
keys_to_names = Hash[names.map{|name| [
|
68
|
+
keys_to_names = Hash[names.map { |name| [normalize_key(name, options), name] }]
|
70
69
|
values = {}
|
71
70
|
|
72
71
|
instrument(:read_multi, names, options) do
|
73
|
-
if raw_values = @data.get_multi(keys_to_names.keys, :
|
72
|
+
if raw_values = @data.get_multi(keys_to_names.keys, raw: true)
|
74
73
|
raw_values.each do |key, value|
|
75
74
|
entry = deserialize_entry(value)
|
76
75
|
values[keys_to_names[key]] = entry.value unless entry.expired?
|
@@ -85,7 +84,7 @@ module ActiveSupport
|
|
85
84
|
|
86
85
|
def cas(name, options = nil)
|
87
86
|
options = merged_options(options)
|
88
|
-
key =
|
87
|
+
key = normalize_key(name, options)
|
89
88
|
|
90
89
|
instrument(:cas, name, options) do
|
91
90
|
@data.cas(key, expiration(options), cas_raw?(options)) do |raw_value|
|
@@ -101,21 +100,30 @@ module ActiveSupport
|
|
101
100
|
end
|
102
101
|
|
103
102
|
def cas_multi(*names)
|
104
|
-
|
105
103
|
options = names.extract_options!
|
104
|
+
return if names.empty?
|
105
|
+
|
106
106
|
options = merged_options(options)
|
107
|
-
keys_to_names = Hash[names.map{|name| [
|
107
|
+
keys_to_names = Hash[names.map { |name| [normalize_key(name, options), name] }]
|
108
108
|
|
109
109
|
instrument(:cas_multi, names, options) do
|
110
110
|
@data.cas(keys_to_names.keys, expiration(options), cas_raw?(options)) do |raw_values|
|
111
111
|
values = {}
|
112
|
+
|
112
113
|
raw_values.each do |key, raw_value|
|
113
114
|
entry = deserialize_entry(raw_value)
|
114
115
|
values[keys_to_names[key]] = entry.value unless entry.expired?
|
115
116
|
end
|
117
|
+
|
116
118
|
values = yield values
|
119
|
+
|
117
120
|
break true if read_only
|
118
|
-
|
121
|
+
|
122
|
+
serialized_values = values.map do |name, value|
|
123
|
+
[normalize_key(name, options), serialize_entry(Entry.new(value, options), options).first]
|
124
|
+
end
|
125
|
+
|
126
|
+
Hash[serialized_values]
|
119
127
|
end
|
120
128
|
end
|
121
129
|
rescue *NONFATAL_EXCEPTIONS => e
|
@@ -125,8 +133,8 @@ module ActiveSupport
|
|
125
133
|
|
126
134
|
def increment(name, amount = 1, options = nil) # :nodoc:
|
127
135
|
options = merged_options(options)
|
128
|
-
instrument(:increment, name, :
|
129
|
-
@data.incr(
|
136
|
+
instrument(:increment, name, amount: amount) do
|
137
|
+
@data.incr(normalize_key(name, options), amount)
|
130
138
|
end
|
131
139
|
rescue *NONFATAL_EXCEPTIONS => e
|
132
140
|
@data.log_exception(e)
|
@@ -135,8 +143,8 @@ module ActiveSupport
|
|
135
143
|
|
136
144
|
def decrement(name, amount = 1, options = nil) # :nodoc:
|
137
145
|
options = merged_options(options)
|
138
|
-
instrument(:decrement, name, :
|
139
|
-
@data.decr(
|
146
|
+
instrument(:decrement, name, amount: amount) do
|
147
|
+
@data.decr(normalize_key(name, options), amount)
|
140
148
|
end
|
141
149
|
rescue *NONFATAL_EXCEPTIONS => e
|
142
150
|
@data.log_exception(e)
|
@@ -144,14 +152,18 @@ module ActiveSupport
|
|
144
152
|
end
|
145
153
|
|
146
154
|
def clear(options = nil)
|
147
|
-
instrument(
|
155
|
+
ActiveSupport::Notifications.instrument("cache_clear.active_support", options || {}) do
|
156
|
+
@data.flush_all
|
157
|
+
end
|
148
158
|
end
|
149
159
|
|
150
160
|
def stats
|
151
|
-
instrument(
|
161
|
+
ActiveSupport::Notifications.instrument("cache_stats.active_support") do
|
162
|
+
@data.stats
|
163
|
+
end
|
152
164
|
end
|
153
165
|
|
154
|
-
def exist?(*
|
166
|
+
def exist?(*)
|
155
167
|
!!super
|
156
168
|
end
|
157
169
|
|
@@ -163,70 +175,90 @@ module ActiveSupport
|
|
163
175
|
end
|
164
176
|
|
165
177
|
protected
|
166
|
-
def read_entry(key, options) # :nodoc:
|
167
|
-
deserialize_entry(@data.get(escape_key(key), true))
|
168
|
-
rescue *NONFATAL_EXCEPTIONS => e
|
169
|
-
@data.log_exception(e)
|
170
|
-
nil
|
171
|
-
end
|
172
178
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
rescue *NONFATAL_EXCEPTIONS => e
|
180
|
-
@data.log_exception(e)
|
181
|
-
false
|
182
|
-
end
|
179
|
+
def read_entry(key, _options) # :nodoc:
|
180
|
+
deserialize_entry(@data.get(escape_key(key), true))
|
181
|
+
rescue *NONFATAL_EXCEPTIONS => e
|
182
|
+
@data.log_exception(e)
|
183
|
+
nil
|
184
|
+
end
|
183
185
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
186
|
+
def write_entry(key, entry, options) # :nodoc:
|
187
|
+
return true if read_only
|
188
|
+
method = options && options[:unless_exist] ? :add : :set
|
189
|
+
expires_in = expiration(options)
|
190
|
+
value, raw = serialize_entry(entry, options)
|
191
|
+
@data.send(method, escape_key(key), value, expires_in, raw)
|
192
|
+
rescue *NONFATAL_EXCEPTIONS => e
|
193
|
+
@data.log_exception(e)
|
194
|
+
false
|
195
|
+
end
|
196
|
+
|
197
|
+
def delete_entry(key, _options) # :nodoc:
|
198
|
+
return true if read_only
|
199
|
+
@data.delete(escape_key(key))
|
200
|
+
true
|
201
|
+
rescue *NONFATAL_EXCEPTIONS => e
|
202
|
+
@data.log_exception(e)
|
203
|
+
false
|
204
|
+
end
|
192
205
|
|
193
206
|
private
|
194
207
|
|
208
|
+
if ActiveSupport::VERSION::MAJOR < 5
|
209
|
+
def normalize_key(key, options)
|
210
|
+
escape_key(namespaced_key(key, options))
|
211
|
+
end
|
212
|
+
|
195
213
|
def escape_key(key)
|
196
214
|
key = key.to_s.dup
|
197
215
|
key = key.force_encoding(Encoding::ASCII_8BIT)
|
198
|
-
key = key.gsub(ESCAPE_KEY_CHARS){ |match| "%#{match.getbyte(0).to_s(16).upcase}" }
|
216
|
+
key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
|
199
217
|
key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250
|
200
218
|
key
|
201
219
|
end
|
202
|
-
|
203
|
-
def
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
end
|
220
|
+
else
|
221
|
+
def normalize_key(key, options)
|
222
|
+
key = super.dup
|
223
|
+
key = key.force_encoding(Encoding::ASCII_8BIT)
|
224
|
+
key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
|
225
|
+
key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250
|
226
|
+
key
|
210
227
|
end
|
211
228
|
|
212
|
-
def
|
213
|
-
|
214
|
-
[entry, options[:raw]]
|
229
|
+
def escape_key(key)
|
230
|
+
key
|
215
231
|
end
|
232
|
+
end
|
216
233
|
|
217
|
-
|
218
|
-
|
234
|
+
def deserialize_entry(raw_value)
|
235
|
+
if raw_value
|
236
|
+
entry = begin
|
237
|
+
Marshal.load(raw_value)
|
238
|
+
rescue
|
239
|
+
raw_value
|
240
|
+
end
|
241
|
+
entry.is_a?(Entry) ? entry : Entry.new(entry)
|
219
242
|
end
|
243
|
+
end
|
220
244
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
245
|
+
def serialize_entry(entry, options)
|
246
|
+
entry = entry.value.to_s if options[:raw]
|
247
|
+
[entry, options[:raw]]
|
248
|
+
end
|
249
|
+
|
250
|
+
def cas_raw?(options)
|
251
|
+
options[:raw]
|
252
|
+
end
|
229
253
|
|
254
|
+
def expiration(options)
|
255
|
+
expires_in = options[:expires_in].to_i
|
256
|
+
if expires_in > 0 && !options[:raw]
|
257
|
+
# Set the memcache expire a few minutes in the future to support race condition ttls on read
|
258
|
+
expires_in += 5.minutes
|
259
|
+
end
|
260
|
+
expires_in
|
261
|
+
end
|
230
262
|
end
|
231
263
|
end
|
232
264
|
end
|
data/lib/memcached_store.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: memcached_store
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Camilo Lopez
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2016-
|
14
|
+
date: 2016-12-01 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: activesupport
|
@@ -19,14 +19,14 @@ dependencies:
|
|
19
19
|
requirements:
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '
|
22
|
+
version: '4'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: '
|
29
|
+
version: '4'
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: memcached
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -65,25 +65,15 @@ executables: []
|
|
65
65
|
extensions: []
|
66
66
|
extra_rdoc_files: []
|
67
67
|
files:
|
68
|
-
- ".gitignore"
|
69
|
-
- ".travis.yml"
|
70
|
-
- Gemfile
|
71
68
|
- LICENSE
|
72
69
|
- README.md
|
73
|
-
- Rakefile
|
74
70
|
- lib/active_support/cache/memcached_snappy_store.rb
|
75
71
|
- lib/active_support/cache/memcached_store.rb
|
76
72
|
- lib/memcached_store.rb
|
77
|
-
- lib/memcached_store/memcached_safety.rb
|
78
73
|
- lib/memcached_store/version.rb
|
79
|
-
- memcached_store.gemspec
|
80
|
-
- shipit.rubygems.yml
|
81
|
-
- test/test_helper.rb
|
82
|
-
- test/test_memcached_safety.rb
|
83
|
-
- test/test_memcached_snappy_store.rb
|
84
|
-
- test/test_memcached_store.rb
|
85
74
|
homepage: https://github.com/Shopify/memcached_store/
|
86
|
-
licenses:
|
75
|
+
licenses:
|
76
|
+
- MIT
|
87
77
|
metadata: {}
|
88
78
|
post_install_message:
|
89
79
|
rdoc_options: []
|
@@ -93,7 +83,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
83
|
requirements:
|
94
84
|
- - ">="
|
95
85
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
86
|
+
version: 2.2.0
|
97
87
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
88
|
requirements:
|
99
89
|
- - ">="
|
@@ -105,8 +95,4 @@ rubygems_version: 2.5.1
|
|
105
95
|
signing_key:
|
106
96
|
specification_version: 4
|
107
97
|
summary: Plugin-able Memcached adapters to add features (compression, safety)
|
108
|
-
test_files:
|
109
|
-
- test/test_helper.rb
|
110
|
-
- test/test_memcached_safety.rb
|
111
|
-
- test/test_memcached_snappy_store.rb
|
112
|
-
- test/test_memcached_store.rb
|
98
|
+
test_files: []
|