readthis 0.5.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/PERFORMANCE.md +1 -1
- data/README.md +11 -7
- data/benchmarks/multi.rb +11 -0
- data/lib/readthis/cache.rb +71 -11
- data/lib/readthis/version.rb +1 -1
- data/spec/readthis/cache_spec.rb +21 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 39c546b2720bf3e983c15c8fe9d7a60fea06cf75
|
4
|
+
data.tar.gz: 0a41d522ce9f5a096bf967a344d3e71ab1c01b56
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 669ad6115ac86b13bb8ff8108a839d6c9890fbf4ca456ee8bd5f06dfe53578532559a8507a9cdd8a7ed7458341def6acee7e7e051b479fef2828c3312b0f32b8
|
7
|
+
data.tar.gz: 15ebfbe02678355846db403aa48b800212aeb5115c05b3b14107ba603db2e74445403ad24988b2161eaff7c5d8e0662b8c7fa1bab2a0927f290ccbf943fa5648
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## v0.5.1 2014-12-30
|
2
|
+
|
3
|
+
- Fixed: The `clear` method now accepts an argument for compatibility with other
|
4
|
+
caches. The argument is not actually used for anything.
|
5
|
+
- Changed: The `delete` method will always return a boolean value rather than an
|
6
|
+
integer.
|
7
|
+
- Changed: Avoid multiple instrumentation calls and pool checkouts within
|
8
|
+
`fetch_multi` calls.
|
9
|
+
|
1
10
|
## v0.5.0 2014-12-12
|
2
11
|
|
3
12
|
- Added: All read and write operations are marshalled to and from storage. This
|
data/PERFORMANCE.md
CHANGED
data/README.md
CHANGED
@@ -7,10 +7,14 @@
|
|
7
7
|
|
8
8
|
Readthis is a drop in replacement for any ActiveSupport compliant cache, but
|
9
9
|
emphasizes performance and simplicity. It takes some cues from Dalli (connection
|
10
|
-
pooling), the popular Memcache client.
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
pooling), the popular Memcache client.
|
11
|
+
|
12
|
+
For any new projects there isn't any reason to stick with Memcached. Redis is
|
13
|
+
as fast, if not faster in many scenarios, and is far more likely to be used
|
14
|
+
elsewhere in the stack. See [this Stack Overflow post][stackoverflow] for more
|
15
|
+
details.
|
16
|
+
|
17
|
+
[stackoverflow]: http://stackoverflow.com/questions/10558465/memcache-vs-redis
|
14
18
|
|
15
19
|
## Footprint & Performance
|
16
20
|
|
@@ -52,6 +56,8 @@ redis database, which defaults to 0. For example, using database 2:
|
|
52
56
|
REDIS_URL=redis://localhost:6379/2
|
53
57
|
```
|
54
58
|
|
59
|
+
[store]: http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html
|
60
|
+
|
55
61
|
### Compression
|
56
62
|
|
57
63
|
Compression can be enabled for all actions by passing the `compress` flag. By
|
@@ -72,7 +78,7 @@ config.cache_store = :readthis_store, ENV.fetch('REDIS_URL'), {
|
|
72
78
|
|
73
79
|
Readthis uses Ruby's `Marshal` module for dumping and loading all values by
|
74
80
|
default. This isn't always the fastest option, depending on your use case it may
|
75
|
-
be desirable to use a faster but
|
81
|
+
be desirable to use a faster but less flexible marshaller.
|
76
82
|
|
77
83
|
Use Oj for JSON marshalling, extremely fast, limited types:
|
78
84
|
|
@@ -93,5 +99,3 @@ Readthis supports all of standard cache methods except for the following:
|
|
93
99
|
* `cleanup` - redis does this with ttl for us already
|
94
100
|
* `delete_matched` - you really don't want to perform key matching operations
|
95
101
|
in redis. They are linear time and only support basic globbing.
|
96
|
-
|
97
|
-
[store]: http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html
|
data/benchmarks/multi.rb
CHANGED
@@ -5,10 +5,12 @@ Bundler.setup
|
|
5
5
|
require 'benchmark/ips'
|
6
6
|
require 'dalli'
|
7
7
|
require 'redis-activesupport'
|
8
|
+
require 'active_support/cache/memory_store'
|
8
9
|
require 'active_support/cache/dalli_store'
|
9
10
|
require 'readthis'
|
10
11
|
|
11
12
|
REDIS_URL = 'redis://localhost:6379/11'
|
13
|
+
memory = ActiveSupport::Cache::MemoryStore.new(expires_in: 60, namespace: 'mm')
|
12
14
|
dalli = ActiveSupport::Cache::DalliStore.new('localhost', namespace: 'da', pool_size: 5, expires_in: 60)
|
13
15
|
redisas = ActiveSupport::Cache::RedisStore.new(REDIS_URL + '/ra', expires_in: 60)
|
14
16
|
readthis = Readthis::Cache.new(REDIS_URL, namespace: 'rd', expires_in: 60)
|
@@ -16,12 +18,17 @@ readthis = Readthis::Cache.new(REDIS_URL, namespace: 'rd', expires_in: 60)
|
|
16
18
|
('a'..'z').each do |key|
|
17
19
|
value = key * 1024
|
18
20
|
|
21
|
+
memory.write(key, value)
|
19
22
|
dalli.write(key, value)
|
20
23
|
readthis.write(key, value)
|
21
24
|
redisas.write(key, value)
|
22
25
|
end
|
23
26
|
|
24
27
|
Benchmark.ips do |x|
|
28
|
+
x.report 'memory:read-multi' do
|
29
|
+
memory.read_multi(*('a'..'z'))
|
30
|
+
end
|
31
|
+
|
25
32
|
x.report 'readthis:read-multi' do
|
26
33
|
readthis.read_multi(*('a'..'z'))
|
27
34
|
end
|
@@ -38,6 +45,10 @@ Benchmark.ips do |x|
|
|
38
45
|
end
|
39
46
|
|
40
47
|
Benchmark.ips do |x|
|
48
|
+
x.report 'memory:fetch-multi' do
|
49
|
+
memory.fetch_multi(*('a'..'z')) { |_| 'missing' }
|
50
|
+
end
|
51
|
+
|
41
52
|
x.report 'readthis:fetch-multi' do
|
42
53
|
readthis.fetch_multi(*('a'..'z')) { |_| 'missing' }
|
43
54
|
end
|
data/lib/readthis/cache.rb
CHANGED
@@ -75,22 +75,41 @@ module Readthis
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
+
# Writes data to the cache using the given key. Will overwrite whatever
|
79
|
+
# value is already stored at that key.
|
80
|
+
#
|
81
|
+
# @param [String] Key for lookup
|
82
|
+
# @param [Hash] Optional overrides
|
83
|
+
#
|
84
|
+
# @example
|
85
|
+
#
|
86
|
+
# cache.write('some-key', 'a bunch of text') # => 'OK'
|
87
|
+
# cache.write('some-key', 'short lived', expires_in: 60) # => 'OK'
|
88
|
+
# cache.write('some-key', 'lives elsehwere', namespace: 'cache') # => 'OK'
|
89
|
+
#
|
78
90
|
def write(key, value, options = {})
|
79
|
-
options
|
80
|
-
namespaced = namespaced_key(key, options)
|
91
|
+
options = merged_options(options)
|
81
92
|
|
82
93
|
invoke(:write, key) do |store|
|
83
|
-
|
84
|
-
store.setex(namespaced, expiration, entity.dump(value))
|
85
|
-
else
|
86
|
-
store.set(namespaced, entity.dump(value))
|
87
|
-
end
|
94
|
+
write_entity(key, value, store, options)
|
88
95
|
end
|
89
96
|
end
|
90
97
|
|
98
|
+
# Delete the value stored at the specified key. Returns `true` if
|
99
|
+
# anything was deleted, `false` otherwise.
|
100
|
+
#
|
101
|
+
# @params [String] The key for lookup
|
102
|
+
# @params [Hash] Optional overrides
|
103
|
+
#
|
104
|
+
# @example
|
105
|
+
#
|
106
|
+
# cache.delete('existing-key') # => true
|
107
|
+
# cache.delete('random-key') # => false
|
91
108
|
def delete(key, options = {})
|
109
|
+
namespaced = namespaced_key(key, merged_options(options))
|
110
|
+
|
92
111
|
invoke(:delete, key) do |store|
|
93
|
-
store.del(
|
112
|
+
store.del(namespaced) > 0
|
94
113
|
end
|
95
114
|
end
|
96
115
|
|
@@ -162,9 +181,20 @@ module Readthis
|
|
162
181
|
# and filling in any cache misses. All read and write operations are
|
163
182
|
# executed atomically.
|
164
183
|
#
|
184
|
+
# @overload fetch_multi(keys)
|
185
|
+
# Return all values fro the given keys, applying the block to the key
|
186
|
+
# when a value is missing.
|
187
|
+
# @param [String] One or more keys to fetch
|
188
|
+
#
|
189
|
+
# @example
|
190
|
+
#
|
165
191
|
# cache.fetch_multi('alpha', 'beta') do |key|
|
166
192
|
# "#{key}-was-missing"
|
167
193
|
# end
|
194
|
+
#
|
195
|
+
# cache.fetch_multi('a', 'b', expires_in: 60) do |key|
|
196
|
+
# key * 2
|
197
|
+
# end
|
168
198
|
def fetch_multi(*keys)
|
169
199
|
results = read_multi(*keys)
|
170
200
|
options = merged_options(extract_options!(keys))
|
@@ -173,8 +203,8 @@ module Readthis
|
|
173
203
|
store.pipelined do
|
174
204
|
results.each do |key, value|
|
175
205
|
if value.nil?
|
176
|
-
value = yield
|
177
|
-
|
206
|
+
value = yield(key)
|
207
|
+
write_entity(key, value, store, options)
|
178
208
|
results[key] = value
|
179
209
|
end
|
180
210
|
end
|
@@ -184,16 +214,46 @@ module Readthis
|
|
184
214
|
end
|
185
215
|
end
|
186
216
|
|
217
|
+
# Returns `true` if the cache contains an entry for the given key.
|
218
|
+
#
|
219
|
+
# @param [String] Key for lookup
|
220
|
+
# @param [Hash] Optional overrides
|
221
|
+
#
|
222
|
+
# @example
|
223
|
+
#
|
224
|
+
# cache.exist?('some-key') # => false
|
225
|
+
# cache.exist?('some-key', namespace: 'cache') # => true
|
226
|
+
#
|
187
227
|
def exist?(key, options = {})
|
188
228
|
invoke(:exist?, key) do |store|
|
189
229
|
store.exists(namespaced_key(key, merged_options(options)))
|
190
230
|
end
|
191
231
|
end
|
192
232
|
|
193
|
-
|
233
|
+
# Clear the entire cache. This flushes the current database, no
|
234
|
+
# globbing is applied.
|
235
|
+
#
|
236
|
+
# @param [Hash] Options, only present for compatibility.
|
237
|
+
#
|
238
|
+
# @example
|
239
|
+
#
|
240
|
+
# cache.clear #=> 'OK'
|
241
|
+
def clear(options = {})
|
194
242
|
invoke(:clear, '*', &:flushdb)
|
195
243
|
end
|
196
244
|
|
245
|
+
protected
|
246
|
+
|
247
|
+
def write_entity(key, value, store, options)
|
248
|
+
namespaced = namespaced_key(key, options)
|
249
|
+
|
250
|
+
if expiration = options[:expires_in]
|
251
|
+
store.setex(namespaced, expiration, entity.dump(value))
|
252
|
+
else
|
253
|
+
store.set(namespaced, entity.dump(value))
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
197
257
|
private
|
198
258
|
|
199
259
|
def alter(key, amount, options)
|
data/lib/readthis/version.rb
CHANGED
data/spec/readthis/cache_spec.rb
CHANGED
@@ -158,6 +158,22 @@ RSpec.describe Readthis::Cache do
|
|
158
158
|
'b' => 'bb',
|
159
159
|
'c' => 3,
|
160
160
|
)
|
161
|
+
|
162
|
+
expect(cache.read('b')).to eq('bb')
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'uses passed options' do
|
166
|
+
cache.write('a', 1, namespace: 'alph')
|
167
|
+
|
168
|
+
results = cache.fetch_multi('a', 'b', namespace: 'alph') { |key| key }
|
169
|
+
|
170
|
+
expect(results).to eq(
|
171
|
+
'a' => 1,
|
172
|
+
'b' => 'b'
|
173
|
+
)
|
174
|
+
|
175
|
+
expect(cache.read('b')).to be_nil
|
176
|
+
expect(cache.read('b', namespace: 'alph')).not_to be_nil
|
161
177
|
end
|
162
178
|
end
|
163
179
|
|
@@ -175,10 +191,14 @@ RSpec.describe Readthis::Cache do
|
|
175
191
|
describe '#delete' do
|
176
192
|
it 'deletes an existing key' do
|
177
193
|
cache.write('not-long', 'for this world')
|
178
|
-
cache.delete('not-long')
|
179
194
|
|
195
|
+
expect(cache.delete('not-long')).to be_truthy
|
180
196
|
expect(cache.read('not-long')).to be_nil
|
181
197
|
end
|
198
|
+
|
199
|
+
it 'safely returns false if nothing is deleted' do
|
200
|
+
expect(cache.delete('no-such-key')).to be_falsy
|
201
|
+
end
|
182
202
|
end
|
183
203
|
|
184
204
|
describe '#increment' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: readthis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Parker Selbert
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|