readthis 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/PERFORMANCE.md +73 -0
- data/README.md +16 -41
- data/benchmarks/compressed.rb +73 -0
- data/benchmarks/multi.rb +9 -7
- data/lib/readthis/cache.rb +62 -8
- data/lib/readthis/compressor.rb +41 -0
- data/lib/readthis/expanders.rb +22 -14
- data/lib/readthis/version.rb +1 -1
- data/readthis.gemspec +1 -0
- data/spec/readthis/cache_spec.rb +48 -0
- data/spec/readthis/compressor_spec.rb +38 -0
- data/spec/readthis/expanders_spec.rb +1 -1
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f5a03b37918d41dfd0e30151cb3049638df50aa8
|
4
|
+
data.tar.gz: 9c80690714866bc6992eafa9c137e785cf72a88d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9297ce7d8525ecdc2715399b3b657fb3cc12e581e4e9d067d762f7e9a134ffe6a35e48f6867bf7a26902f5dc48b6106e5c6dd012bc5e16372ad1b715fa6faa60
|
7
|
+
data.tar.gz: d431742cd9f908aabdd06dfeaab2ed99b7165158fff51c57c947d9857d336e2a5c82fe42edae15a41c7cdd8043b3fc950c09e6fd60feb4d69c571fb71e2fe6e3
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
## v0.4.0 2014-12-11
|
2
|
+
|
3
|
+
- Added: Force the use of `hiredis` as the adapter. It is dramatically faster,
|
4
|
+
but prevents the project from being used in `jruby`. If we get interest from
|
5
|
+
some `jruby` projects we can soften the requirement.
|
6
|
+
- Added: Compression! Adheres to the `ActiveSupport::Store` documentation.
|
7
|
+
- Fixed: Gracefully handle `nil` passed as `options` to any cache method.
|
8
|
+
|
1
9
|
## v0.3.0 2014-12-01
|
2
10
|
|
3
11
|
- Added: Use `to_param` for key expansion, only when available. Makes it
|
data/PERFORMANCE.md
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
Results from the various benchmarks in `./bencharks`. Hardware doesnt't matter
|
2
|
+
much, as we're simply looking for a comparison against other libraries and prior
|
3
|
+
verions.
|
4
|
+
|
5
|
+
## Footprint
|
6
|
+
|
7
|
+
Footprint compared to `redis-activesupport`:
|
8
|
+
|
9
|
+
```
|
10
|
+
# Total allocated objects after require
|
11
|
+
readthis: 20602
|
12
|
+
redis-activesupport: 78630
|
13
|
+
```
|
14
|
+
|
15
|
+
## Performance
|
16
|
+
|
17
|
+
Performance compared to `dalli` and `redis-activesupport`:
|
18
|
+
|
19
|
+
```
|
20
|
+
Raw Read Multi:
|
21
|
+
Calculating -------------------------------------
|
22
|
+
readthis:read-multi 500.000 i/100ms
|
23
|
+
redisas:read-multi 95.000 i/100ms
|
24
|
+
dalli:read-multi 97.000 i/100ms
|
25
|
+
-------------------------------------------------
|
26
|
+
readthis:read-multi 5.286k (± 2.7%) i/s - 26.500k
|
27
|
+
redisas:read-multi 959.405 (± 4.2%) i/s - 4.845k
|
28
|
+
dalli:read-multi 978.803 (± 2.1%) i/s - 4.947k
|
29
|
+
|
30
|
+
Comparison:
|
31
|
+
readthis:read-multi: 5286.0 i/s
|
32
|
+
dalli:read-multi: 978.8 i/s - 5.40x slower
|
33
|
+
redisas:read-multi: 959.4 i/s - 5.51x slower
|
34
|
+
|
35
|
+
Raw Fetch Multi:
|
36
|
+
Calculating -------------------------------------
|
37
|
+
readthis:fetch-multi 448.000 i/100ms
|
38
|
+
redisas:fetch-multi 84.000 i/100ms
|
39
|
+
dalli:fetch-multi 99.000 i/100ms
|
40
|
+
-------------------------------------------------
|
41
|
+
readthis:fetch-multi 4.682k (± 2.4%) i/s - 23.744k
|
42
|
+
redisas:fetch-multi 848.101 (± 3.2%) i/s - 4.284k
|
43
|
+
dalli:fetch-multi 1.006k (± 2.4%) i/s - 5.049k
|
44
|
+
|
45
|
+
Comparison:
|
46
|
+
readthis:fetch-multi: 4682.4 i/s
|
47
|
+
dalli:fetch-multi: 1005.6 i/s - 4.66x slower
|
48
|
+
redisas:fetch-multi: 848.1 i/s - 5.52x slower
|
49
|
+
|
50
|
+
Compressed Writes:
|
51
|
+
Calculating -------------------------------------
|
52
|
+
readthis:write 1.003k i/100ms
|
53
|
+
dalli:write 913.000 i/100ms
|
54
|
+
-------------------------------------------------
|
55
|
+
readthis:write 11.095k (± 5.7%) i/s - 56.168k
|
56
|
+
dalli:write 9.829k (± 1.8%) i/s - 49.302k
|
57
|
+
|
58
|
+
Comparison:
|
59
|
+
readthis:write: 11095.5 i/s
|
60
|
+
dalli:write: 9828.8 i/s - 1.13x slower
|
61
|
+
|
62
|
+
Compressed Read Multi:
|
63
|
+
Calculating -------------------------------------
|
64
|
+
readthis:read_multi 446.000 i/100ms
|
65
|
+
dalli:read_multi 97.000 i/100ms
|
66
|
+
-------------------------------------------------
|
67
|
+
readthis:read_multi 4.728k (± 4.6%) i/s - 23.638k
|
68
|
+
dalli:read_multi 985.986 (± 3.9%) i/s - 4.947k
|
69
|
+
|
70
|
+
Comparison:
|
71
|
+
readthis:read_multi: 4728.3 i/s
|
72
|
+
dalli:read_multi: 986.0 i/s - 4.80x slower
|
73
|
+
```
|
data/README.md
CHANGED
@@ -13,46 +13,7 @@ behavior for `fetch_multi`.
|
|
13
13
|
|
14
14
|
## Footprint & Performance
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
```
|
19
|
-
# Total allocated objects after require
|
20
|
-
readthis: 19,964
|
21
|
-
redis-activesupport: 78,630
|
22
|
-
```
|
23
|
-
|
24
|
-
Performance compared to `dalli` and `redis-activesupport` for \*multi
|
25
|
-
operations:
|
26
|
-
|
27
|
-
```
|
28
|
-
Calculating -------------------------------------
|
29
|
-
readthis:read-multi 118.000 i/100ms
|
30
|
-
redisas:read-multi 94.000 i/100ms
|
31
|
-
dalli:read-multi 92.000 i/100ms
|
32
|
-
-------------------------------------------------
|
33
|
-
readthis:read-multi 1.206k (± 4.6%) i/s - 6.018k
|
34
|
-
redisas:read-multi 973.086 (± 4.4%) i/s - 4.888k
|
35
|
-
dalli:read-multi 949.348 (± 4.1%) i/s - 4.784k
|
36
|
-
|
37
|
-
Comparison:
|
38
|
-
readthis:read-multi: 1206.0 i/s
|
39
|
-
redisas:read-multi: 973.1 i/s - 1.24x slower
|
40
|
-
dalli:read-multi: 949.3 i/s - 1.27x slower
|
41
|
-
|
42
|
-
Calculating -------------------------------------
|
43
|
-
readthis:fetch-multi 114.000 i/100ms
|
44
|
-
redisas:fetch-multi 82.000 i/100ms
|
45
|
-
dalli:fetch-multi 97.000 i/100ms
|
46
|
-
-------------------------------------------------
|
47
|
-
readthis:fetch-multi 1.157k (± 5.0%) i/s - 5.814k
|
48
|
-
redisas:fetch-multi 829.211 (± 4.2%) i/s - 4.182k
|
49
|
-
dalli:fetch-multi 979.081 (± 3.8%) i/s - 4.947k
|
50
|
-
|
51
|
-
Comparison:
|
52
|
-
readthis:fetch-multi: 1157.2 i/s
|
53
|
-
dalli:fetch-multi: 979.1 i/s - 1.18x slower
|
54
|
-
redisas:fetch-multi: 829.2 i/s - 1.40x slower
|
55
|
-
```
|
16
|
+
See [Performance][PERFORMANCE.md]
|
56
17
|
|
57
18
|
## Installation
|
58
19
|
|
@@ -86,10 +47,24 @@ You'll want to use a specific database for caching, just in case you need to
|
|
86
47
|
clear the cache entirely. Appending a number between 0 and 15 will specify the
|
87
48
|
redis database, which defaults to 0. For example, using database 2:
|
88
49
|
|
89
|
-
```
|
50
|
+
```bash
|
90
51
|
REDIS_URL=redis://localhost:6379/2
|
91
52
|
```
|
92
53
|
|
54
|
+
Compression can be enabled for all actions by passing the `compress` flag. By
|
55
|
+
default all values greater than 1024k will be compressed automatically. If there
|
56
|
+
is any content has not been stored with compression, or perhaps was compressed
|
57
|
+
but is beneath the compression threshold, it will be passed through as is. This
|
58
|
+
means it is safe to enable or change compression with an existing cache. There
|
59
|
+
will be a decoding performance penalty in this case, but it should be minor.
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
config.cache_store = :readthis_store, ENV.fetch('REDIS_URL'), {
|
63
|
+
compress: true,
|
64
|
+
compression_threshold: 2.kilobytes
|
65
|
+
}
|
66
|
+
```
|
67
|
+
|
93
68
|
## Differences
|
94
69
|
|
95
70
|
Readthis supports all of standard cache methods except for the following:
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
|
3
|
+
Bundler.setup
|
4
|
+
|
5
|
+
require 'benchmark/ips'
|
6
|
+
require 'dalli'
|
7
|
+
require 'active_support'
|
8
|
+
require 'active_support/cache/dalli_store'
|
9
|
+
require 'readthis'
|
10
|
+
|
11
|
+
dalli = ActiveSupport::Cache::DalliStore.new(
|
12
|
+
'localhost',
|
13
|
+
pool_size: 5,
|
14
|
+
compressed: true,
|
15
|
+
compression_threshold: 8
|
16
|
+
)
|
17
|
+
|
18
|
+
readthis = Readthis::Cache.new(
|
19
|
+
'redis://localhost:6379/11',
|
20
|
+
pool_size: 5,
|
21
|
+
compressed: true,
|
22
|
+
compression_threshold: 128
|
23
|
+
)
|
24
|
+
|
25
|
+
KEY = 'key'
|
26
|
+
TEXT = <<-TEXT
|
27
|
+
An abstract cache store class. There are multiple cache store implementations, each having its own additional features. See the classes under the ActiveSupport::Cache module, e.g. ActiveSupport::Cache::MemCacheStore. MemCacheStore is currently the most popular cache store for large production websites.
|
28
|
+
Some implementations may not support all methods beyond the basic cache methods of fetch, write, read, exist?, and delete.
|
29
|
+
ActiveSupport::Cache::Store can store any serializable Ruby object.
|
30
|
+
cache = ActiveSupport::Cache::MemoryStore.new
|
31
|
+
cache.read('city') # => nil
|
32
|
+
cache.write('city', "Duckburgh")
|
33
|
+
cache.read('city') # => "Duckburgh"
|
34
|
+
Keys are always translated into Strings and are case sensitive. When an object is specified as a key and has a cache_key method defined, this method will be called to define the key. Otherwise, the to_param method will be called. Hashes and Arrays can also be used as keys. The elements will be delimited by slashes, and the elements within a Hash will be sorted by key so they are consistent.
|
35
|
+
cache.read('city') == cache.read(:city) # => true
|
36
|
+
Nil values can be cached.
|
37
|
+
If your cache is on a shared infrastructure, you can define a namespace for your cache entries. If a namespace is defined, it will be prefixed on to every key. The namespace can be either a static value or a Proc. If it is a Proc, it will be invoked when each key is evaluated so that you can use application logic to invalidate keys.
|
38
|
+
cache.namespace = -> { @last_mod_time } # Set the namespace to a variable
|
39
|
+
@last_mod_time = Time.now # Invalidate the entire cache by changing namespace
|
40
|
+
Caches can also store values in a compressed format to save space and reduce time spent sending data. Since there is overhead, values must be large enough to warrant compression. To turn on compression either pass compress: true in the initializer or as an option to fetch or write. To specify the threshold at which to compress values, set the :compress_threshold option. The default threshold is 16K.
|
41
|
+
TEXT
|
42
|
+
|
43
|
+
puts 'Compressed Writes:'
|
44
|
+
Benchmark.ips do |x|
|
45
|
+
x.report 'readthis:write' do
|
46
|
+
readthis.write(KEY, TEXT)
|
47
|
+
end
|
48
|
+
|
49
|
+
x.report 'dalli:write' do
|
50
|
+
dalli.write(KEY, TEXT)
|
51
|
+
end
|
52
|
+
|
53
|
+
x.compare!
|
54
|
+
end
|
55
|
+
|
56
|
+
puts 'Compressed Read Multi:'
|
57
|
+
MULTI_KEY = (1..30).to_a
|
58
|
+
MULTI_KEY.each do |key|
|
59
|
+
readthis.write(key, TEXT)
|
60
|
+
dalli.write(key, TEXT)
|
61
|
+
end
|
62
|
+
|
63
|
+
Benchmark.ips do |x|
|
64
|
+
x.report 'readthis:read_multi' do
|
65
|
+
readthis.read_multi(*MULTI_KEY)
|
66
|
+
end
|
67
|
+
|
68
|
+
x.report 'dalli:read_multi' do
|
69
|
+
dalli.read_multi(*MULTI_KEY)
|
70
|
+
end
|
71
|
+
|
72
|
+
x.compare!
|
73
|
+
end
|
data/benchmarks/multi.rb
CHANGED
@@ -8,15 +8,17 @@ require 'redis-activesupport'
|
|
8
8
|
require 'active_support/cache/dalli_store'
|
9
9
|
require 'readthis'
|
10
10
|
|
11
|
-
|
12
|
-
dalli = ActiveSupport::Cache::DalliStore.new('localhost', namespace: 'da', pool_size: 5)
|
13
|
-
redisas = ActiveSupport::Cache::RedisStore.new(
|
14
|
-
readthis = Readthis::Cache.new(
|
11
|
+
REDIS_URL = 'redis://localhost:6379/11'
|
12
|
+
dalli = ActiveSupport::Cache::DalliStore.new('localhost', namespace: 'da', pool_size: 5, expires_in: 60)
|
13
|
+
redisas = ActiveSupport::Cache::RedisStore.new(REDIS_URL + '/ra', expires_in: 60)
|
14
|
+
readthis = Readthis::Cache.new(REDIS_URL, namespace: 'rd', expires_in: 60)
|
15
15
|
|
16
16
|
('a'..'z').each do |key|
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
value = key * 1024
|
18
|
+
|
19
|
+
dalli.write(key, value)
|
20
|
+
readthis.write(key, value)
|
21
|
+
redisas.write(key, value)
|
20
22
|
end
|
21
23
|
|
22
24
|
Benchmark.ips do |x|
|
data/lib/readthis/cache.rb
CHANGED
@@ -1,11 +1,19 @@
|
|
1
|
+
require 'readthis/compressor'
|
1
2
|
require 'readthis/expanders'
|
2
3
|
require 'readthis/notifications'
|
3
4
|
require 'redis'
|
5
|
+
require 'hiredis'
|
4
6
|
require 'connection_pool'
|
5
7
|
|
6
8
|
module Readthis
|
7
9
|
class Cache
|
8
|
-
attr_reader :
|
10
|
+
attr_reader :compress,
|
11
|
+
:compression_threshold,
|
12
|
+
:expires_in,
|
13
|
+
:namespace,
|
14
|
+
:pool
|
15
|
+
|
16
|
+
alias_method :compress?, :compress
|
9
17
|
|
10
18
|
# Provide a class level lookup of the proper notifications module.
|
11
19
|
# Instrumention is expected to occur within applications that have
|
@@ -22,19 +30,34 @@ module Readthis
|
|
22
30
|
# Creates a new Readthis::Cache object with the given redis URL. The URL
|
23
31
|
# is parsed by the redis client directly.
|
24
32
|
#
|
33
|
+
# @param url [String] A redis compliant url with necessary connection details
|
34
|
+
# @option options [String] :namespace Prefix used to namespace entries
|
35
|
+
# @option options [Number] :expires_in The number of seconds until an entry expires
|
36
|
+
# @option options [Boolean] :compress Enable or disable automatic compression
|
37
|
+
# @option options [Number] :compression_threshold The size a string must be for compression
|
38
|
+
#
|
39
|
+
# @example Create a new cache instance
|
25
40
|
# Readthis::Cache.new('redis://localhost:6379/0', namespace: 'cache')
|
41
|
+
#
|
42
|
+
# @example Create a compressed cache instance
|
43
|
+
# Readthis::Cache.new('redis://localhost:6379/0', compress: true, compression_threshold: 2048)
|
44
|
+
#
|
26
45
|
def initialize(url, options = {})
|
27
46
|
@expires_in = options.fetch(:expires_in, nil)
|
28
|
-
@namespace = options.fetch(:namespace,
|
47
|
+
@namespace = options.fetch(:namespace, nil)
|
48
|
+
@compress = options.fetch(:compress, false)
|
49
|
+
@compression_threshold = options.fetch(:compression_threshold, 1024)
|
29
50
|
|
30
51
|
@pool = ConnectionPool.new(pool_options(options)) do
|
31
|
-
Redis.new(url: url)
|
52
|
+
Redis.new(url: url, driver: :hiredis)
|
32
53
|
end
|
33
54
|
end
|
34
55
|
|
35
56
|
def read(key, options = {})
|
36
57
|
invoke(:read, key) do |store|
|
37
|
-
store.get(namespaced_key(key, merged_options(options)))
|
58
|
+
value = store.get(namespaced_key(key, merged_options(options)))
|
59
|
+
|
60
|
+
decompressed(value)
|
38
61
|
end
|
39
62
|
end
|
40
63
|
|
@@ -44,9 +67,9 @@ module Readthis
|
|
44
67
|
|
45
68
|
invoke(:write, key) do |store|
|
46
69
|
if expiration = options[:expires_in]
|
47
|
-
store.setex(namespaced, expiration, value)
|
70
|
+
store.setex(namespaced, expiration, compressed(value))
|
48
71
|
else
|
49
|
-
store.set(namespaced, value)
|
72
|
+
store.set(namespaced, compressed(value))
|
50
73
|
end
|
51
74
|
end
|
52
75
|
end
|
@@ -85,7 +108,9 @@ module Readthis
|
|
85
108
|
mapping = keys.map { |key| namespaced_key(key, options) }
|
86
109
|
|
87
110
|
invoke(:read_multi, keys) do |store|
|
88
|
-
|
111
|
+
values = decompressed_multi(store.mget(mapping))
|
112
|
+
|
113
|
+
keys.zip(values).to_h
|
89
114
|
end
|
90
115
|
end
|
91
116
|
|
@@ -127,6 +152,34 @@ module Readthis
|
|
127
152
|
|
128
153
|
private
|
129
154
|
|
155
|
+
def compressed(value)
|
156
|
+
if compress?
|
157
|
+
compressor.compress(value)
|
158
|
+
else
|
159
|
+
value
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def decompressed(value)
|
164
|
+
if compress?
|
165
|
+
compressor.decompress(value)
|
166
|
+
else
|
167
|
+
value
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def decompressed_multi(values)
|
172
|
+
if compress?
|
173
|
+
values.map { |value| compressor.decompress(value) }
|
174
|
+
else
|
175
|
+
values
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def compressor
|
180
|
+
@compressor ||= Readthis::Compressor.new(threshold: compression_threshold)
|
181
|
+
end
|
182
|
+
|
130
183
|
def instrument(operation, key)
|
131
184
|
name = "cache_#{operation}.active_support"
|
132
185
|
payload = { key: key }
|
@@ -145,6 +198,7 @@ module Readthis
|
|
145
198
|
end
|
146
199
|
|
147
200
|
def merged_options(options)
|
201
|
+
options = options || {}
|
148
202
|
options[:namespace] ||= namespace
|
149
203
|
options[:expires_in] ||= expires_in
|
150
204
|
options
|
@@ -156,7 +210,7 @@ module Readthis
|
|
156
210
|
end
|
157
211
|
|
158
212
|
def namespaced_key(key, options)
|
159
|
-
Readthis::Expanders.
|
213
|
+
Readthis::Expanders.namespace_key(key, options[:namespace])
|
160
214
|
end
|
161
215
|
end
|
162
216
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'zlib'
|
2
|
+
|
3
|
+
module Readthis
|
4
|
+
class Compressor
|
5
|
+
attr_reader :threshold
|
6
|
+
|
7
|
+
# Create a new Readthis::Compressor object that pivots on the provided
|
8
|
+
# threshold value.
|
9
|
+
#
|
10
|
+
# @param threshold [Number] the threshold size required for compression
|
11
|
+
def initialize(threshold: 1024)
|
12
|
+
@threshold = threshold
|
13
|
+
end
|
14
|
+
|
15
|
+
# Compress a value if its size is greater or equal to the current threshold.
|
16
|
+
#
|
17
|
+
# @param value [String] a string to compress
|
18
|
+
def compress(value)
|
19
|
+
if value.size >= threshold
|
20
|
+
Zlib::Deflate.deflate(value)
|
21
|
+
else
|
22
|
+
value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Decompress a previously compressed object. It will attempt to decode a
|
27
|
+
# value regardless of whether it has been compressed, but will rescue
|
28
|
+
# decoding errors.
|
29
|
+
#
|
30
|
+
# @param value [String] a possibly compressed string to decompress
|
31
|
+
def decompress(value)
|
32
|
+
if value.size >= threshold
|
33
|
+
Zlib::Inflate.inflate(value)
|
34
|
+
else
|
35
|
+
value
|
36
|
+
end
|
37
|
+
rescue Zlib::Error
|
38
|
+
value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/readthis/expanders.rb
CHANGED
@@ -1,20 +1,28 @@
|
|
1
1
|
module Readthis
|
2
2
|
module Expanders
|
3
|
-
def self.
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
3
|
+
def self.expand_key(key)
|
4
|
+
case
|
5
|
+
when key.respond_to?(:cache_key)
|
6
|
+
key.cache_key
|
7
|
+
when key.is_a?(Array)
|
8
|
+
key.flat_map { |elem| expand_key(elem) }.join('/')
|
9
|
+
when key.is_a?(Hash)
|
10
|
+
key.sort_by { |key, _| key.to_s }.map { |key, val| "#{key}=#{val}" }.join('/')
|
11
|
+
when key.respond_to?(:to_param)
|
12
|
+
key.to_param
|
13
|
+
else
|
14
|
+
key
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.namespace_key(key, namespace)
|
19
|
+
expanded = expand_key(key)
|
16
20
|
|
17
|
-
|
21
|
+
if namespace
|
22
|
+
"#{namespace}:#{expanded}"
|
23
|
+
else
|
24
|
+
expanded
|
25
|
+
end
|
18
26
|
end
|
19
27
|
end
|
20
28
|
end
|
data/lib/readthis/version.rb
CHANGED
data/readthis.gemspec
CHANGED
data/spec/readthis/cache_spec.rb
CHANGED
@@ -20,6 +20,17 @@ RSpec.describe Readthis::Cache do
|
|
20
20
|
|
21
21
|
expect(cache.expires_in).to eq(10)
|
22
22
|
end
|
23
|
+
|
24
|
+
it 'stores compression parameters' do
|
25
|
+
cache = Readthis::Cache.new(
|
26
|
+
url,
|
27
|
+
compress: true,
|
28
|
+
compression_threshold: 8
|
29
|
+
)
|
30
|
+
|
31
|
+
expect(cache.compress).to be_truthy
|
32
|
+
expect(cache.compression_threshold).to eq(8)
|
33
|
+
end
|
23
34
|
end
|
24
35
|
|
25
36
|
describe '#write' do
|
@@ -53,6 +64,43 @@ RSpec.describe Readthis::Cache do
|
|
53
64
|
end
|
54
65
|
end
|
55
66
|
|
67
|
+
describe '#read' do
|
68
|
+
it 'gracefully handles nil options' do
|
69
|
+
expect { cache.read('whatever', nil) }.not_to raise_error
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#compress' do
|
74
|
+
it 'round trips entries when compression is enabled' do
|
75
|
+
com_cache = Readthis::Cache.new(url, compress: true, compression_threshold: 8)
|
76
|
+
raw_cache = Readthis::Cache.new(url)
|
77
|
+
value = 'enough text that it should be compressed'
|
78
|
+
|
79
|
+
com_cache.write('compressed', value)
|
80
|
+
|
81
|
+
expect(raw_cache.read('compressed')).not_to eq(value)
|
82
|
+
expect(com_cache.read('compressed')).to eq(value)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'round trips bulk entries when compression is enabled' do
|
86
|
+
cache = Readthis::Cache.new(url, compress: true, compression_threshold: 8)
|
87
|
+
value = 'also enough text to compress'
|
88
|
+
|
89
|
+
cache.write('comp-a', value)
|
90
|
+
cache.write('comp-b', value)
|
91
|
+
|
92
|
+
expect(cache.read_multi('comp-a', 'comp-b')).to eq(
|
93
|
+
'comp-a' => value,
|
94
|
+
'comp-b' => value
|
95
|
+
)
|
96
|
+
|
97
|
+
expect(cache.fetch_multi('comp-a', 'comp-b')).to eq(
|
98
|
+
'comp-a' => value,
|
99
|
+
'comp-b' => value
|
100
|
+
)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
56
104
|
describe '#fetch' do
|
57
105
|
it 'gets an existing value' do
|
58
106
|
cache.write('great-key', 'great')
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'readthis/compressor'
|
2
|
+
|
3
|
+
RSpec.describe Readthis::Compressor do
|
4
|
+
describe '#compress' do
|
5
|
+
it 'compresses the input' do
|
6
|
+
compressor = Readthis::Compressor.new(threshold: 0)
|
7
|
+
input = 'aaa bbb ccc'
|
8
|
+
output = compressor.compress(input)
|
9
|
+
|
10
|
+
expect(input).not_to eq(output)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'passes input below the threshold size through uncompressed' do
|
14
|
+
compressor = Readthis::Compressor.new(threshold: 1024)
|
15
|
+
input = 'abcdefg'
|
16
|
+
|
17
|
+
expect(compressor.compress(input)).to eq(input)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#decompress' do
|
22
|
+
it 'decompresses compressed data' do
|
23
|
+
compressor = Readthis::Compressor.new(threshold: 0)
|
24
|
+
input = 'aaa bbb ccc'
|
25
|
+
compressed = compressor.compress(input)
|
26
|
+
decompressed = compressor.decompress(compressed)
|
27
|
+
|
28
|
+
expect(decompressed).to eq(input)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'passes through decompression failures' do
|
32
|
+
compressor = Readthis::Compressor.new(threshold: 0)
|
33
|
+
input = 'abcdefg'
|
34
|
+
|
35
|
+
expect(compressor.decompress(input)).to eq(input)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
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.
|
4
|
+
version: 0.4.0
|
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-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: hiredis
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.5'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.5'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: connection_pool
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,19 +108,23 @@ files:
|
|
94
108
|
- CONTRIBUTING.md
|
95
109
|
- Gemfile
|
96
110
|
- LICENSE.txt
|
111
|
+
- PERFORMANCE.md
|
97
112
|
- README.md
|
98
113
|
- Rakefile
|
114
|
+
- benchmarks/compressed.rb
|
99
115
|
- benchmarks/memory.rb
|
100
116
|
- benchmarks/multi.rb
|
101
117
|
- bin/rspec
|
102
118
|
- lib/active_support/cache/readthis_store.rb
|
103
119
|
- lib/readthis.rb
|
104
120
|
- lib/readthis/cache.rb
|
121
|
+
- lib/readthis/compressor.rb
|
105
122
|
- lib/readthis/expanders.rb
|
106
123
|
- lib/readthis/notifications.rb
|
107
124
|
- lib/readthis/version.rb
|
108
125
|
- readthis.gemspec
|
109
126
|
- spec/readthis/cache_spec.rb
|
127
|
+
- spec/readthis/compressor_spec.rb
|
110
128
|
- spec/readthis/expanders_spec.rb
|
111
129
|
- spec/readthis/notifications_spec.rb
|
112
130
|
- spec/spec_helper.rb
|
@@ -136,6 +154,7 @@ specification_version: 4
|
|
136
154
|
summary: Pooled active support compliant caching with redis
|
137
155
|
test_files:
|
138
156
|
- spec/readthis/cache_spec.rb
|
157
|
+
- spec/readthis/compressor_spec.rb
|
139
158
|
- spec/readthis/expanders_spec.rb
|
140
159
|
- spec/readthis/notifications_spec.rb
|
141
160
|
- spec/spec_helper.rb
|