readthis 0.6.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +8 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile +2 -0
- data/README.md +42 -22
- data/benchmarks/driver.rb +19 -0
- data/benchmarks/marshalling.rb +16 -12
- data/benchmarks/memory.rb +3 -3
- data/benchmarks/profile.rb +20 -0
- data/lib/readthis/cache.rb +2 -2
- data/lib/readthis/entity.rb +4 -4
- data/lib/readthis/version.rb +1 -1
- data/readthis.gemspec +2 -2
- data/spec/readthis/cache_spec.rb +18 -0
- metadata +18 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: beeb6a1ebaa7b28ece28bbc82b71a85bd78db186
|
4
|
+
data.tar.gz: 5c1af1a823131e8320c862b4774e285c08ad8525
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f418008520264991ec238afda668dadea6b42724df8b638b858dada82e0bc5263aea0fa3fd6ffc48839da51fd3153fdd436d348de60fa996cfdc7c57750b2c76
|
7
|
+
data.tar.gz: db0e5445ea22524934d4257347ad28584b8fb03c7b2acfa6c543ce655d76d7f96b5af19d2efada2ed6be22a5afad34dcdf10c5132a466bad2b3b5ee00d68be01
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## v0.7.0 2015-08-11
|
2
|
+
|
3
|
+
- Changed: Entity initialization uses an options hash rather than keyword
|
4
|
+
arguments. This allows flexibility with older Ruby versions (1.9) that aren't
|
5
|
+
officially supported.
|
6
|
+
- Changed: There is no longer a hard dependency on `hiredis`, though it is the
|
7
|
+
default. The redis driver can be configured by passing a `driver: :ruby`
|
8
|
+
option through to the constructor.
|
9
|
+
|
1
10
|
## v0.6.2 2015-04-28
|
2
11
|
|
3
12
|
- Fixed: Set expiration during `write_multi`, primarily effecting `fetch_multi`.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,20 +1,19 @@
|
|
1
1
|
[![Gem Version](https://badge.fury.io/rb/readthis.svg)](http://badge.fury.io/rb/readthis)
|
2
2
|
[![Build Status](https://travis-ci.org/sorentwo/readthis.svg?branch=master)](https://travis-ci.org/sorentwo/readthis)
|
3
3
|
[![Code Climate](https://codeclimate.com/github/sorentwo/readthis/badges/gpa.svg)](https://codeclimate.com/github/sorentwo/readthis)
|
4
|
-
[![Coverage Status](https://
|
4
|
+
[![Coverage Status](https://coveralls.io/repos/sorentwo/readthis/badge.svg?branch=master&service=github)](https://coveralls.io/github/sorentwo/readthis?branch=master)
|
5
5
|
|
6
6
|
# Readthis
|
7
7
|
|
8
|
-
Readthis is a drop in replacement for any ActiveSupport compliant cache
|
9
|
-
emphasizes performance and simplicity
|
10
|
-
|
8
|
+
Readthis is a drop in replacement for any ActiveSupport compliant cache. It
|
9
|
+
emphasizes performance and simplicity and takes some cues from Dalli the popular
|
10
|
+
Memcache client.
|
11
11
|
|
12
|
-
For
|
13
|
-
|
14
|
-
elsewhere in the stack. See [this
|
15
|
-
details.
|
12
|
+
For new projects there isn't any reason to stick with Memcached. Redis is as
|
13
|
+
fast, if not faster in many scenarios, and is far more likely to be used
|
14
|
+
elsewhere in the stack. See [this blog post][hp-caching] for more details.
|
16
15
|
|
17
|
-
[
|
16
|
+
[hp-caching]: http://sorentwo.com/2015/07/20/high-performance-caching-with-readthis.html
|
18
17
|
|
19
18
|
## Footprint & Performance
|
20
19
|
|
@@ -31,7 +30,7 @@ gem 'readthis'
|
|
31
30
|
## Usage
|
32
31
|
|
33
32
|
Use it the same way as any other [ActiveSupport::Cache::Store][store]. Within a
|
34
|
-
|
33
|
+
Rails environment config:
|
35
34
|
|
36
35
|
```ruby
|
37
36
|
config.cache_store = :readthis_store, ENV.fetch('REDIS_URL'), {
|
@@ -40,7 +39,7 @@ config.cache_store = :readthis_store, ENV.fetch('REDIS_URL'), {
|
|
40
39
|
}
|
41
40
|
```
|
42
41
|
|
43
|
-
Otherwise you can use it anywhere, without any reliance on ActiveSupport
|
42
|
+
Otherwise you can use it anywhere, without any reliance on `ActiveSupport`:
|
44
43
|
|
45
44
|
```ruby
|
46
45
|
require 'readthis'
|
@@ -48,21 +47,34 @@ require 'readthis'
|
|
48
47
|
cache = Readthis::Cache.new(ENV.fetch('REDIS_URL'), expires_in: 2.weeks.to_i)
|
49
48
|
```
|
50
49
|
|
51
|
-
|
52
|
-
|
53
|
-
|
50
|
+
[store]: http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html
|
51
|
+
|
52
|
+
### Instances & Databases
|
53
|
+
|
54
|
+
An isolated Redis instance that is only used for caching is ideal. Dedicated
|
55
|
+
instances have numerous benefits like: more predictable performance, avoiding
|
56
|
+
expires in favor of LRU, and tuning the persistence mechanism. See [Optimizing
|
57
|
+
Redis Usage for Caching][optimizing-usage] for more details.
|
58
|
+
|
59
|
+
[optimizing-usage]: http://sorentwo.com/2015/07/27/optimizing-redis-usage-for-caching.html
|
60
|
+
|
61
|
+
At the very least you'll want to use a specific database for caching. In the
|
62
|
+
event the database needs to be purged you can do so with a single `clear`
|
63
|
+
command, rather than finding all keys in a namespace and deleting them.
|
64
|
+
Appending a number between 0 and 15 will specify the redis database, which
|
65
|
+
defaults to 0. For example, using database 2:
|
54
66
|
|
55
67
|
```bash
|
56
68
|
REDIS_URL=redis://localhost:6379/2
|
57
69
|
```
|
58
70
|
|
71
|
+
### Expiration
|
72
|
+
|
59
73
|
Be sure to use an integer value when setting expiration time. The default
|
60
74
|
representation of `ActiveSupport::Duration` values won't work when setting
|
61
75
|
expiration time, which will cause all keys to have `-1` as the TTL. Expiration
|
62
76
|
values are always cast as an integer on write.
|
63
77
|
|
64
|
-
[store]: http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html
|
65
|
-
|
66
78
|
### Compression
|
67
79
|
|
68
80
|
Compression can be enabled for all actions by passing the `compress` flag. By
|
@@ -82,8 +94,8 @@ config.cache_store = :readthis_store, ENV.fetch('REDIS_URL'), {
|
|
82
94
|
### Marshalling
|
83
95
|
|
84
96
|
Readthis uses Ruby's `Marshal` module for dumping and loading all values by
|
85
|
-
default. This isn't always the fastest option, depending on your use case it
|
86
|
-
be desirable to use a faster but less flexible marshaller.
|
97
|
+
default. This isn't always the fastest option, and depending on your use case it
|
98
|
+
may be desirable to use a faster but less flexible marshaller.
|
87
99
|
|
88
100
|
Use Oj for JSON marshalling, extremely fast, but supports limited types:
|
89
101
|
|
@@ -91,8 +103,8 @@ Use Oj for JSON marshalling, extremely fast, but supports limited types:
|
|
91
103
|
Readthis::Cache.new(url, marshal: Oj)
|
92
104
|
```
|
93
105
|
|
94
|
-
If
|
95
|
-
marshaller:
|
106
|
+
If all cached data can safely be represented as a string then use the
|
107
|
+
pass-through marshaller:
|
96
108
|
|
97
109
|
```ruby
|
98
110
|
Readthis::Cache.new(url, marshal: Readthis::Passthrough)
|
@@ -102,6 +114,14 @@ Readthis::Cache.new(url, marshal: Readthis::Passthrough)
|
|
102
114
|
|
103
115
|
Readthis supports all of standard cache methods except for the following:
|
104
116
|
|
105
|
-
* `cleanup` -
|
117
|
+
* `cleanup` - Redis does this with TTL or LRU already.
|
106
118
|
* `delete_matched` - you really don't want to perform key matching operations
|
107
|
-
in
|
119
|
+
in Redis. They are linear time and only support basic globbing.
|
120
|
+
|
121
|
+
## Contributing
|
122
|
+
|
123
|
+
1. Fork it
|
124
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
125
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
126
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
127
|
+
5. Create new Pull Request
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
|
3
|
+
Bundler.setup
|
4
|
+
|
5
|
+
require 'benchmark/ips'
|
6
|
+
require 'readthis'
|
7
|
+
|
8
|
+
REDIS_URL = 'redis://localhost:6379/11'
|
9
|
+
native = Readthis::Cache.new(REDIS_URL, driver: :ruby, expires_in: 60)
|
10
|
+
hiredis = Readthis::Cache.new(REDIS_URL, driver: :hiredis, expires_in: 60)
|
11
|
+
|
12
|
+
('a'..'z').each { |key| native.write(key, key * 1024) }
|
13
|
+
|
14
|
+
Benchmark.ips do |x|
|
15
|
+
x.report('native:read-multi') { native.read_multi(*('a'..'z')) }
|
16
|
+
x.report('hiredis:read-multi') { hiredis.read_multi(*('a'..'z')) }
|
17
|
+
|
18
|
+
x.compare!
|
19
|
+
end
|
data/benchmarks/marshalling.rb
CHANGED
@@ -5,33 +5,37 @@ Bundler.setup
|
|
5
5
|
require 'benchmark/ips'
|
6
6
|
require 'json'
|
7
7
|
require 'oj'
|
8
|
+
require 'msgpack'
|
8
9
|
require 'readthis'
|
9
10
|
require 'readthis/passthrough'
|
10
11
|
|
11
12
|
REDIS_URL = 'redis://localhost:6379/11'
|
12
13
|
OPTIONS = { compressed: false }
|
13
14
|
|
14
|
-
readthis_pass
|
15
|
-
readthis_oj
|
16
|
-
|
17
|
-
|
15
|
+
readthis_pass = Readthis::Cache.new(REDIS_URL, OPTIONS.merge(marshal: Readthis::Passthrough))
|
16
|
+
readthis_oj = Readthis::Cache.new(REDIS_URL, OPTIONS.merge(marshal: Oj))
|
17
|
+
readthis_msgpack = Readthis::Cache.new(REDIS_URL, OPTIONS.merge(marshal: MessagePack))
|
18
|
+
readthis_json = Readthis::Cache.new(REDIS_URL, OPTIONS.merge(marshal: JSON))
|
19
|
+
readthis_ruby = Readthis::Cache.new(REDIS_URL, OPTIONS.merge(marshal: Marshal))
|
18
20
|
|
19
21
|
HASH = ('a'..'z').each_with_object({}) { |key, memo| memo[key] = key }
|
20
22
|
|
21
23
|
Benchmark.ips do |x|
|
22
|
-
x.report('pass:hash:dump')
|
23
|
-
x.report('oj:hash:dump')
|
24
|
-
x.report('json:hash:dump')
|
25
|
-
x.report('
|
24
|
+
x.report('pass:hash:dump') { readthis_pass.write('pass', HASH) }
|
25
|
+
x.report('oj:hash:dump') { readthis_oj.write('oj', HASH) }
|
26
|
+
x.report('json:hash:dump') { readthis_json.write('json', HASH) }
|
27
|
+
x.report('msgpack:hash:dump') { readthis_msgpack.write('msgpack', HASH) }
|
28
|
+
x.report('ruby:hash:dump') { readthis_ruby.write('ruby', HASH) }
|
26
29
|
|
27
30
|
x.compare!
|
28
31
|
end
|
29
32
|
|
30
33
|
Benchmark.ips do |x|
|
31
|
-
x.report('pass:hash:load')
|
32
|
-
x.report('oj:hash:load')
|
33
|
-
x.report('json:hash:load')
|
34
|
-
x.report('
|
34
|
+
x.report('pass:hash:load') { readthis_pass.read('pass') }
|
35
|
+
x.report('oj:hash:load') { readthis_oj.read('oj') }
|
36
|
+
x.report('json:hash:load') { readthis_json.read('json') }
|
37
|
+
x.report('msgpack:hash:load') { readthis_msgpack.read('msgpack') }
|
38
|
+
x.report('ruby:hash:load') { readthis_ruby.read('ruby') }
|
35
39
|
|
36
40
|
x.compare!
|
37
41
|
end
|
data/benchmarks/memory.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'bundler'; Bundler.setup
|
2
|
-
a = GC.stat(:
|
2
|
+
a = GC.stat(:total_allocated_objects)
|
3
3
|
|
4
4
|
require 'readthis'
|
5
|
-
b = GC.stat(:
|
5
|
+
b = GC.stat(:total_allocated_objects)
|
6
6
|
|
7
7
|
require 'redis-activesupport'
|
8
|
-
c = GC.stat(:
|
8
|
+
c = GC.stat(:total_allocated_objects)
|
9
9
|
|
10
10
|
puts "readthis: #{b - a}"
|
11
11
|
puts "redis-activesupport: #{c - b}"
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
|
3
|
+
Bundler.setup
|
4
|
+
|
5
|
+
require 'fileutils'
|
6
|
+
require 'stackprof'
|
7
|
+
require 'readthis'
|
8
|
+
|
9
|
+
readthis = Readthis::Cache.new('redis://localhost:6379/11')
|
10
|
+
|
11
|
+
FileUtils.mkdir_p('tmp')
|
12
|
+
readthis.clear
|
13
|
+
|
14
|
+
('a'..'z').each { |key| readthis.write(key, key * 1024) }
|
15
|
+
|
16
|
+
StackProf.run(mode: :object, interval: 500, out: "tmp/stackprof-object.dump") do
|
17
|
+
1000.times do
|
18
|
+
readthis.read_multi(*('a'..'z'))
|
19
|
+
end
|
20
|
+
end
|
data/lib/readthis/cache.rb
CHANGED
@@ -3,7 +3,6 @@ require 'readthis/expanders'
|
|
3
3
|
require 'readthis/notifications'
|
4
4
|
require 'readthis/passthrough'
|
5
5
|
require 'redis'
|
6
|
-
require 'hiredis'
|
7
6
|
require 'connection_pool'
|
8
7
|
|
9
8
|
module Readthis
|
@@ -31,6 +30,7 @@ module Readthis
|
|
31
30
|
# @option [Number] :expires_in The number of seconds until an entry expires
|
32
31
|
# @option [Module] :marshal (Marshal) Any module that responds to `dump` and `load`
|
33
32
|
# @option [String] :namespace Prefix used to namespace entries
|
33
|
+
# @option [Symbol] :driver (:hiredis) Specify a driver to be used for Redis connections
|
34
34
|
# @option [Number] :pool_size (5) The number of threads in the pool
|
35
35
|
# @option [Number] :pool_timeout (5) How long before a thread times out
|
36
36
|
#
|
@@ -51,7 +51,7 @@ module Readthis
|
|
51
51
|
)
|
52
52
|
|
53
53
|
@pool = ConnectionPool.new(pool_options(options)) do
|
54
|
-
Redis.new(url: url, driver: :hiredis)
|
54
|
+
Redis.new(url: url, driver: options.fetch(:driver, :hiredis))
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
data/lib/readthis/entity.rb
CHANGED
@@ -6,10 +6,10 @@ module Readthis
|
|
6
6
|
|
7
7
|
attr_reader :marshal, :compression, :threshold
|
8
8
|
|
9
|
-
def initialize(
|
10
|
-
@marshal = marshal
|
11
|
-
@compression = compress
|
12
|
-
@threshold = threshold
|
9
|
+
def initialize(options = {})
|
10
|
+
@marshal = options.fetch(:marshal, Marshal)
|
11
|
+
@compression = options.fetch(:compress, false)
|
12
|
+
@threshold = options.fetch(:threshold, DEFAULT_THRESHOLD)
|
13
13
|
end
|
14
14
|
|
15
15
|
def dump(value)
|
data/lib/readthis/version.rb
CHANGED
data/readthis.gemspec
CHANGED
@@ -18,10 +18,10 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.require_paths = ['lib']
|
19
19
|
|
20
20
|
spec.add_dependency 'redis', '~> 3.0'
|
21
|
-
spec.add_dependency 'hiredis', '~> 0.5'
|
22
21
|
spec.add_dependency 'connection_pool', '~> 2.1'
|
23
22
|
|
24
23
|
spec.add_development_dependency 'bundler'
|
24
|
+
spec.add_development_dependency 'hiredis', '~> 0.5'
|
25
25
|
spec.add_development_dependency 'rake'
|
26
|
-
spec.add_development_dependency 'rspec',
|
26
|
+
spec.add_development_dependency 'rspec', '~> 3.1'
|
27
27
|
end
|
data/spec/readthis/cache_spec.rb
CHANGED
@@ -22,6 +22,24 @@ RSpec.describe Readthis::Cache do
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
+
describe '#pool' do
|
26
|
+
it 'creates a new redis connection with hiredis' do
|
27
|
+
cache = Readthis::Cache.new(url)
|
28
|
+
|
29
|
+
cache.pool.with do |client|
|
30
|
+
expect(client.client.driver).to be(Redis::Connection::Hiredis)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'creates a new redis connection with a custom driver' do
|
35
|
+
cache = Readthis::Cache.new(url, driver: :ruby)
|
36
|
+
|
37
|
+
cache.pool.with do |client|
|
38
|
+
expect(client.client.driver).to be(Redis::Connection::Ruby)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
25
43
|
describe '#write' do
|
26
44
|
it 'stores strings in the cache' do
|
27
45
|
cache.write('some-key', 'some-value')
|
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.7.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: 2015-
|
11
|
+
date: 2015-08-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -24,20 +24,6 @@ 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'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: connection_pool
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +52,20 @@ dependencies:
|
|
66
52
|
- - ">="
|
67
53
|
- !ruby/object:Gem::Version
|
68
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: hiredis
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.5'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.5'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rake
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -112,9 +112,11 @@ files:
|
|
112
112
|
- README.md
|
113
113
|
- Rakefile
|
114
114
|
- benchmarks/compressed.rb
|
115
|
+
- benchmarks/driver.rb
|
115
116
|
- benchmarks/marshalling.rb
|
116
117
|
- benchmarks/memory.rb
|
117
118
|
- benchmarks/multi.rb
|
119
|
+
- benchmarks/profile.rb
|
118
120
|
- bin/rspec
|
119
121
|
- lib/active_support/cache/readthis_store.rb
|
120
122
|
- lib/readthis.rb
|