redis-slave-read 0.1 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +1 -0
- data/README.md +1 -1
- data/lib/{activesupport/lib/active_support/cache/redis_slave_read_cache.rb → active_support/cache/redis_store_slave_read.rb} +35 -35
- data/lib/redis-slave-read/version.rb +1 -1
- data/redis-slave-read.gemspec +2 -2
- data/spec/interface/hiredis_spec.rb +23 -18
- metadata +17 -24
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 32c1f6b501f4d1f2e013277b17194a45f555e0cf
|
4
|
+
data.tar.gz: d608989faa6527ac97a0ecc88788928dcecb0509
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e1be26d56b3b8a03279ce70954d5a166c916f3751b8b0d01c247470bf2f4232f32ce2c4a81f70e7dde8619b7c9b56f75e8eda1c3f7c5b02e102e55d582ac1948
|
7
|
+
data.tar.gz: fb782d7638f89f69daa000135a04cb841d3b9049baae79962644bb20fc34249edc7a0e83ca49ef36575c804d77b4328a1f7667b22f3cd04f3d748f2c77c7e465
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -23,7 +23,7 @@ Rather than using a Redis instance, create a wrapper that wraps multiple Redis c
|
|
23
23
|
master = Redis.new "localhost:6379"
|
24
24
|
slave1 = Redis.new "localhost:6389"
|
25
25
|
slave2 = Redis.new "localhost:6399"
|
26
|
-
$redis = Redis::SlaveRead::Interface::
|
26
|
+
$redis = Redis::SlaveRead::Interface::Hiredis.new(master: master, slaves: [slave1, slave2])
|
27
27
|
|
28
28
|
Make sure that your slaves are set to be slaved to the master, like `slaveof localhost 6379`
|
29
29
|
|
@@ -1,29 +1,12 @@
|
|
1
|
+
require 'connection_pool'
|
2
|
+
|
1
3
|
module ActiveSupport
|
2
4
|
module Cache
|
3
|
-
class
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
# # => host: localhost, port: 6379, db: 0
|
9
|
-
#
|
10
|
-
# RedisStore.new "example.com"
|
11
|
-
# # => host: example.com, port: 6379, db: 0
|
12
|
-
#
|
13
|
-
# RedisStore.new "example.com:23682"
|
14
|
-
# # => host: example.com, port: 23682, db: 0
|
15
|
-
#
|
16
|
-
# RedisStore.new "example.com:23682/1"
|
17
|
-
# # => host: example.com, port: 23682, db: 1
|
18
|
-
#
|
19
|
-
# RedisStore.new "example.com:23682/1/theplaylist"
|
20
|
-
# # => host: example.com, port: 23682, db: 1, namespace: theplaylist
|
21
|
-
#
|
22
|
-
# RedisStore.new "localhost:6379/0", "localhost:6380/0"
|
23
|
-
# # => instantiate a cluster
|
24
|
-
def initialize(*addresses)
|
25
|
-
@data = ::Redis::Factory.create(addresses)
|
26
|
-
super(addresses.extract_options!)
|
5
|
+
class RedisStoreSlaveRead < Store
|
6
|
+
def initialize(options = {})
|
7
|
+
@pool_options = options
|
8
|
+
init_pool @pool_options
|
9
|
+
@options = {}
|
27
10
|
end
|
28
11
|
|
29
12
|
def write(name, value, options = nil)
|
@@ -43,7 +26,7 @@ module ActiveSupport
|
|
43
26
|
instrument(:delete_matched, matcher.inspect) do
|
44
27
|
matcher = key_matcher(matcher, options)
|
45
28
|
begin
|
46
|
-
!(keys =
|
29
|
+
@pool.with {|s| !(keys = s.keys(matcher)).empty? && s.del(*keys) }
|
47
30
|
rescue Errno::ECONNREFUSED => e
|
48
31
|
false
|
49
32
|
end
|
@@ -57,7 +40,7 @@ module ActiveSupport
|
|
57
40
|
# cache.read_multi "rabbit", "white-rabbit"
|
58
41
|
# cache.read_multi "rabbit", "white-rabbit", :raw => true
|
59
42
|
def read_multi(*names)
|
60
|
-
values = @
|
43
|
+
values = @pool.with {|s| s.mget(*names) }
|
61
44
|
|
62
45
|
# Remove the options hash before mapping keys to values
|
63
46
|
names.extract_options!
|
@@ -90,7 +73,7 @@ module ActiveSupport
|
|
90
73
|
# cache.read "rabbit", :raw => true # => "1"
|
91
74
|
def increment(key, amount = 1)
|
92
75
|
instrument(:increment, key, :amount => amount) do
|
93
|
-
@
|
76
|
+
@pool.with {|s| s.incrby key, amount }
|
94
77
|
end
|
95
78
|
end
|
96
79
|
|
@@ -117,36 +100,53 @@ module ActiveSupport
|
|
117
100
|
# cache.read "rabbit", :raw => true # => "-1"
|
118
101
|
def decrement(key, amount = 1)
|
119
102
|
instrument(:decrement, key, :amount => amount) do
|
120
|
-
@
|
103
|
+
@pool.with {|s| s.decrby key, amount }
|
121
104
|
end
|
122
105
|
end
|
123
106
|
|
124
107
|
# Clear all the data from the store.
|
125
108
|
def clear
|
126
109
|
instrument(:clear, nil, nil) do
|
127
|
-
@
|
110
|
+
@pool.with {|s| s.flushdb }
|
128
111
|
end
|
129
112
|
end
|
130
113
|
|
131
114
|
def stats
|
132
|
-
@
|
115
|
+
@pool.with {|s| s.info }
|
133
116
|
end
|
134
117
|
|
135
|
-
# Force client reconnection, useful
|
118
|
+
# Force client reconnection, useful for apps deployed on forking servers.
|
136
119
|
def reconnect
|
137
|
-
@
|
120
|
+
init_pool @pool_options
|
121
|
+
end
|
122
|
+
|
123
|
+
def expire(key, expiry)
|
124
|
+
@pool.with {|s| s.expire key, expiry }
|
138
125
|
end
|
139
126
|
|
140
127
|
protected
|
128
|
+
|
129
|
+
def init_pool(options)
|
130
|
+
interface = options.fetch(:interface, ::Redis::SlaveRead::Interface::Hiredis)
|
131
|
+
@pool.shutdown {|node| node.disconnect } if @pool
|
132
|
+
@pool = ConnectionPool.new(:size => options.fetch(:pool_size, 1), :timeout => options.fetch(:pool_timeout, 3)) do
|
133
|
+
interface.new(
|
134
|
+
master: ::Redis::Store::Factory.create(options[:master]),
|
135
|
+
slaves: options[:slaves].map {|s| ::Redis::Store::Factory.create(s) },
|
136
|
+
read_master: false
|
137
|
+
)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
141
|
def write_entry(key, entry, options)
|
142
142
|
method = options && options[:unless_exist] ? :setnx : :set
|
143
|
-
@
|
143
|
+
@pool.with {|s| s.send method, key, entry, options }
|
144
144
|
rescue Errno::ECONNREFUSED => e
|
145
145
|
false
|
146
146
|
end
|
147
147
|
|
148
148
|
def read_entry(key, options)
|
149
|
-
entry = @
|
149
|
+
entry = @pool.with {|s| s.get key, options }
|
150
150
|
if entry
|
151
151
|
entry.is_a?(ActiveSupport::Cache::Entry) ? entry : ActiveSupport::Cache::Entry.new(entry)
|
152
152
|
end
|
@@ -160,7 +160,7 @@ module ActiveSupport
|
|
160
160
|
# It's really needed and use
|
161
161
|
#
|
162
162
|
def delete_entry(key, options)
|
163
|
-
@
|
163
|
+
@pool.with {|s| s.del key }
|
164
164
|
rescue Errno::ECONNREFUSED => e
|
165
165
|
false
|
166
166
|
end
|
data/redis-slave-read.gemspec
CHANGED
@@ -17,6 +17,6 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
18
|
gem.require_paths = ["lib"]
|
19
19
|
|
20
|
-
gem.
|
21
|
-
gem.
|
20
|
+
gem.add_runtime_dependency 'redis', '>= 0', '>= 0'
|
21
|
+
gem.add_runtime_dependency 'connection_pool'
|
22
22
|
end
|
@@ -7,15 +7,15 @@ describe Redis::SlaveRead::Interface::Hiredis do
|
|
7
7
|
subject { described_class.new master: master, slaves: slaves }
|
8
8
|
|
9
9
|
it "should distribute reads between all available nodes" do
|
10
|
-
master.
|
11
|
-
slaves[0].
|
12
|
-
slaves[1].
|
10
|
+
expect(master).to receive(:get).once
|
11
|
+
expect(slaves[0]).to receive(:get).once
|
12
|
+
expect(slaves[1]).to receive(:get).once
|
13
13
|
|
14
14
|
3.times { subject.get "foo" }
|
15
15
|
end
|
16
16
|
|
17
17
|
it "should always send non-reads to the master" do
|
18
|
-
master.
|
18
|
+
expect(master).to receive(:set).exactly(3).times
|
19
19
|
|
20
20
|
3.times { subject.set "foo", "bar" }
|
21
21
|
end
|
@@ -24,9 +24,9 @@ describe Redis::SlaveRead::Interface::Hiredis do
|
|
24
24
|
subject { described_class.new master: master, slaves: slaves, read_master: false }
|
25
25
|
|
26
26
|
it "should distribute reads between all available slaves" do
|
27
|
-
master.
|
28
|
-
slaves[1].
|
29
|
-
slaves[0].
|
27
|
+
expect(master).to receive(:get).never
|
28
|
+
expect(slaves[1]).to receive(:get).twice
|
29
|
+
expect(slaves[0]).to receive(:get).once
|
30
30
|
|
31
31
|
3.times { subject.get "foo" }
|
32
32
|
end
|
@@ -34,7 +34,7 @@ describe Redis::SlaveRead::Interface::Hiredis do
|
|
34
34
|
|
35
35
|
context "when in a multi block" do
|
36
36
|
it "sends all commands to the master" do
|
37
|
-
master.
|
37
|
+
expect(master).to receive(:get).twice
|
38
38
|
|
39
39
|
subject.multi do
|
40
40
|
2.times { subject.get "foo" }
|
@@ -44,7 +44,7 @@ describe Redis::SlaveRead::Interface::Hiredis do
|
|
44
44
|
|
45
45
|
context "when in a pipelined block" do
|
46
46
|
it "sends all commands to the master" do
|
47
|
-
master.
|
47
|
+
expect(master).to receive(:get).twice
|
48
48
|
|
49
49
|
subject.pipelined do
|
50
50
|
2.times { subject.get "foo" }
|
@@ -54,23 +54,28 @@ describe Redis::SlaveRead::Interface::Hiredis do
|
|
54
54
|
|
55
55
|
context "commands that distribute to all nodes" do
|
56
56
|
it "should distribute to each node" do
|
57
|
-
master.
|
58
|
-
slaves.each {|slave| slave.
|
57
|
+
expect(master).to receive(:select).once
|
58
|
+
slaves.each {|slave| expect(slave).to receive(:select).once }
|
59
59
|
subject.send(:select)
|
60
60
|
end
|
61
61
|
|
62
62
|
it "should set the DB on each node" do
|
63
63
|
subject.select 4
|
64
|
-
master.client.db.
|
65
|
-
slaves[0].client.db.
|
66
|
-
slaves[1].client.db.
|
64
|
+
expect(master.client.db).to eq 4
|
65
|
+
expect(slaves[0].client.db).to eq 4
|
66
|
+
expect(slaves[1].client.db).to eq 4
|
67
67
|
end
|
68
68
|
|
69
|
-
it "should disconnect each client" do
|
69
|
+
it "should connect and disconnect each client" do
|
70
|
+
subject.connect
|
71
|
+
expect(!!master.client.connected?).to be_truthy
|
72
|
+
expect(!!slaves[0].client.connected?).to be_truthy
|
73
|
+
expect(!!slaves[1].client.connected?).to be_truthy
|
74
|
+
|
70
75
|
subject.disconnect
|
71
|
-
!!master.client.connected
|
72
|
-
!!slaves[0].client.connected
|
73
|
-
!!slaves[1].client.connected
|
76
|
+
expect(!!master.client.connected?).to be_falsey
|
77
|
+
expect(!!slaves[0].client.connected?).to be_falsey
|
78
|
+
expect(!!slaves[1].client.connected?).to be_falsey
|
74
79
|
end
|
75
80
|
end
|
76
81
|
end
|
metadata
CHANGED
@@ -1,48 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-slave-read
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
version: '0.1'
|
4
|
+
version: 0.2.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Chris Heald
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2016-04-27 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
|
-
|
14
|
+
name: redis
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ">="
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
20
|
+
type: :runtime
|
22
21
|
prerelease: false
|
23
22
|
version_requirements: !ruby/object:Gem::Requirement
|
24
|
-
none: false
|
25
23
|
requirements:
|
26
|
-
- -
|
24
|
+
- - ">="
|
27
25
|
- !ruby/object:Gem::Version
|
28
26
|
version: '0'
|
29
|
-
name: redis
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
|
-
|
28
|
+
name: connection_pool
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - ">="
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
34
|
+
type: :runtime
|
38
35
|
prerelease: false
|
39
36
|
version_requirements: !ruby/object:Gem::Requirement
|
40
|
-
none: false
|
41
37
|
requirements:
|
42
|
-
- -
|
38
|
+
- - ">="
|
43
39
|
- !ruby/object:Gem::Version
|
44
40
|
version: '0'
|
45
|
-
name: redis
|
46
41
|
description: Provides load balancing of reads in a cluster of Redis replicas
|
47
42
|
email:
|
48
43
|
- cheald@gmail.com
|
@@ -50,12 +45,12 @@ executables: []
|
|
50
45
|
extensions: []
|
51
46
|
extra_rdoc_files: []
|
52
47
|
files:
|
53
|
-
- .gitignore
|
48
|
+
- ".gitignore"
|
54
49
|
- Gemfile
|
55
50
|
- LICENSE.txt
|
56
51
|
- README.md
|
57
52
|
- Rakefile
|
58
|
-
- lib/
|
53
|
+
- lib/active_support/cache/redis_store_slave_read.rb
|
59
54
|
- lib/redis-slave-read.rb
|
60
55
|
- lib/redis-slave-read/interface/base.rb
|
61
56
|
- lib/redis-slave-read/interface/hiredis.rb
|
@@ -65,29 +60,27 @@ files:
|
|
65
60
|
- spec/spec_helper.rb
|
66
61
|
homepage: ''
|
67
62
|
licenses: []
|
63
|
+
metadata: {}
|
68
64
|
post_install_message:
|
69
65
|
rdoc_options: []
|
70
66
|
require_paths:
|
71
67
|
- lib
|
72
68
|
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
69
|
requirements:
|
75
|
-
- -
|
70
|
+
- - ">="
|
76
71
|
- !ruby/object:Gem::Version
|
77
72
|
version: '0'
|
78
73
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
-
none: false
|
80
74
|
requirements:
|
81
|
-
- -
|
75
|
+
- - ">="
|
82
76
|
- !ruby/object:Gem::Version
|
83
77
|
version: '0'
|
84
78
|
requirements: []
|
85
79
|
rubyforge_project:
|
86
|
-
rubygems_version:
|
80
|
+
rubygems_version: 2.5.1
|
87
81
|
signing_key:
|
88
|
-
specification_version:
|
82
|
+
specification_version: 4
|
89
83
|
summary: Provides load balancing of reads in a cluster of Redis replicas
|
90
84
|
test_files:
|
91
85
|
- spec/interface/hiredis_spec.rb
|
92
86
|
- spec/spec_helper.rb
|
93
|
-
has_rdoc:
|