redis_ring 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/Gemfile.lock +9 -3
- data/config/redis.conf.erb +4 -6
- data/lib/redis_ring/application.rb +29 -29
- data/lib/redis_ring/background_thread.rb +45 -0
- data/lib/redis_ring/cli.rb +3 -6
- data/lib/redis_ring/configuration.rb +2 -1
- data/lib/redis_ring/http_client.rb +23 -0
- data/lib/redis_ring/master.rb +154 -0
- data/lib/redis_ring/master_rpc.rb +33 -0
- data/lib/redis_ring/node.rb +66 -0
- data/lib/redis_ring/process_manager.rb +33 -19
- data/lib/redis_ring/shard_config.rb +18 -0
- data/lib/redis_ring/slave.rb +62 -0
- data/lib/redis_ring/slave_rpc.rb +42 -0
- data/lib/redis_ring/version.rb +1 -1
- data/lib/redis_ring/web_interface.rb +63 -2
- data/lib/redis_ring/zookeeper_connection.rb +73 -0
- data/lib/redis_ring/zookeeper_observer.rb +47 -0
- data/lib/redis_ring.rb +11 -0
- data/redis_ring.gemspec +2 -0
- data/spec/cluster_builder.rb +224 -0
- data/spec/fakes/fake_http_client.rb +39 -0
- data/spec/fakes/fake_master_rpc.rb +20 -0
- data/spec/fakes/fake_node_provider.rb +2 -0
- data/spec/fakes/fake_process_manager.rb +19 -0
- data/spec/fakes/fake_slave_rpc.rb +36 -0
- data/spec/fakes/fake_zookeeper_connection.rb +2 -0
- data/spec/redis_ring/application_spec.rb +15 -13
- data/spec/redis_ring/master_rpc_spec.rb +20 -0
- data/spec/redis_ring/master_spec.rb +174 -0
- data/spec/redis_ring/node_spec.rb +53 -0
- data/spec/redis_ring/slave_rpc_spec.rb +46 -0
- data/spec/redis_ring/slave_spec.rb +81 -0
- data/spec/spec_helper.rb +20 -1
- data/spec/test.conf +3 -0
- metadata +54 -6
@@ -0,0 +1,174 @@
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
describe RedisRing::Master do
|
4
|
+
|
5
|
+
describe :is_master do
|
6
|
+
before(:each) do
|
7
|
+
@master = RedisRing::Master.new(nil, nil, nil)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should not be master initially" do
|
11
|
+
@master.is_master?.should be_false
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should be master after became_master event" do
|
15
|
+
@master.became_master
|
16
|
+
|
17
|
+
@master.is_master?.should be_true
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should not be master after no_longer_is_master" do
|
21
|
+
@master.became_master
|
22
|
+
@master.no_longer_is_master
|
23
|
+
|
24
|
+
@master.is_master?.should be_false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe :reassign_shards do
|
29
|
+
before(:each) do
|
30
|
+
@builder = ClusterBuilder.new do |b|
|
31
|
+
b.node("node0").port(6401)
|
32
|
+
b.node("node1").port(6402)
|
33
|
+
b.node("node2").port(6403)
|
34
|
+
b.node("node3").port(6404)
|
35
|
+
b.node("node4").port(6405)
|
36
|
+
end
|
37
|
+
|
38
|
+
@started_shards = []
|
39
|
+
@stopped_shards = []
|
40
|
+
@shards_per_node = Hash.new(0)
|
41
|
+
@stopped_on_node = Hash.new(0)
|
42
|
+
|
43
|
+
@builder.on_start_shard do |node_id, shard_no|
|
44
|
+
@started_shards << shard_no
|
45
|
+
@shards_per_node[node_id] += 1
|
46
|
+
end
|
47
|
+
|
48
|
+
@builder.on_stop_shard do |node_id, shard_no|
|
49
|
+
@stopped_shards << shard_no
|
50
|
+
@stopped_on_node[node_id] += 1
|
51
|
+
end
|
52
|
+
|
53
|
+
@master = RedisRing::Master.new(@builder.fake_connection, 16, @builder.fake_provider)
|
54
|
+
@master.became_master
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should assign shards fairly to all nodes" do
|
58
|
+
@master.nodes_changed(@builder.node_ids)
|
59
|
+
|
60
|
+
@started_shards.sort.should == (0...16).to_a
|
61
|
+
@shards_per_node.values.reduce(&:+).should == 16
|
62
|
+
@shards_per_node.values.uniq.sort.should == [3, 4]
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should do nothing when is not a master" do
|
66
|
+
@master.no_longer_is_master
|
67
|
+
|
68
|
+
@master.nodes_changed(@builder.node_ids)
|
69
|
+
|
70
|
+
@started_shards.should be_empty
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should not reassign anything when nothing has changed" do
|
74
|
+
@master.nodes_changed(@builder.node_ids)
|
75
|
+
@started_shards = []
|
76
|
+
@shards_per_node = Hash.new(0)
|
77
|
+
|
78
|
+
@master.nodes_changed(@builder.node_ids)
|
79
|
+
|
80
|
+
@started_shards.should be_empty
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should ignore not joined nodes, without assigning their portion o shards elsewhere" do
|
84
|
+
@builder.node("node0").joined(false)
|
85
|
+
|
86
|
+
@master.nodes_changed(@builder.node_ids)
|
87
|
+
|
88
|
+
@started_shards.size.should == 12
|
89
|
+
@shards_per_node["node0"].should == 0
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should assign shards to the freshly joined node" do
|
93
|
+
@builder.node("node0").joined(false)
|
94
|
+
@master.nodes_changed(@builder.node_ids)
|
95
|
+
@started_shards = []
|
96
|
+
@shards_per_node = Hash.new(0)
|
97
|
+
@builder.node("node0").joined(true)
|
98
|
+
|
99
|
+
@master.node_joined("node0")
|
100
|
+
|
101
|
+
@started_shards.size.should == 4
|
102
|
+
@shards_per_node["node0"].should == 4
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should not over-asign nodes if they are already running some shards" do
|
106
|
+
@builder.node("node0").running_shards([0, 1, 2])
|
107
|
+
@builder.node("node1").running_shards([3, 4, 5, 6])
|
108
|
+
|
109
|
+
@master.nodes_changed(@builder.node_ids)
|
110
|
+
|
111
|
+
@started_shards.size.should == 16 - 7
|
112
|
+
((0..6).to_a - @started_shards).should == (0..6).to_a
|
113
|
+
@shards_per_node["node0"].should == 1
|
114
|
+
@shards_per_node["node1"].should == 0
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should reassign the shards that belonged to a node that crashed" do
|
118
|
+
@master.nodes_changed(@builder.node_ids)
|
119
|
+
@started_shards = []
|
120
|
+
@shards_per_node = Hash.new(0)
|
121
|
+
@builder.nodes.delete("node0")
|
122
|
+
|
123
|
+
@master.nodes_changed(@builder.node_ids)
|
124
|
+
|
125
|
+
@started_shards.size.should == 4
|
126
|
+
@shards_per_node.values.uniq.should == [1]
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should stop shards that are running on more than one node" do
|
130
|
+
@builder.node("node0").running_shards([0, 1, 2])
|
131
|
+
@builder.node("node1").running_shards([0, 1])
|
132
|
+
@builder.node("node2").running_shards([0, 2])
|
133
|
+
|
134
|
+
@master.nodes_changed(@builder.node_ids)
|
135
|
+
|
136
|
+
@stopped_shards.uniq.sort.should == [0, 1, 2]
|
137
|
+
@stopped_shards.size.should == 4
|
138
|
+
([0, 1, 2] - @started_shards).should == [0, 1, 2]
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
describe :status do
|
144
|
+
before(:each) do
|
145
|
+
@builder = ClusterBuilder.new do |b|
|
146
|
+
b.node("node0").host("a.example.com").port(6400)
|
147
|
+
b.node("node1").host("b.example.com").port(6400)
|
148
|
+
end
|
149
|
+
|
150
|
+
@master = RedisRing::Master.new(@builder.fake_connection, 8, @builder.fake_provider)
|
151
|
+
@master.became_master
|
152
|
+
@master.nodes_changed(@builder.node_ids)
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should return the ring_size" do
|
156
|
+
@master.status[:ring_size].should == 8
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should return the shards" do
|
160
|
+
@master.status[:shards].should == {
|
161
|
+
0 => { :host => "a.example.com", :port => 6401, :status => :running },
|
162
|
+
1 => { :host => "a.example.com", :port => 6402, :status => :running },
|
163
|
+
2 => { :host => "a.example.com", :port => 6403, :status => :running },
|
164
|
+
3 => { :host => "a.example.com", :port => 6404, :status => :running },
|
165
|
+
4 => { :host => "b.example.com", :port => 6405, :status => :running },
|
166
|
+
5 => { :host => "b.example.com", :port => 6406, :status => :running },
|
167
|
+
6 => { :host => "b.example.com", :port => 6407, :status => :running },
|
168
|
+
7 => { :host => "b.example.com", :port => 6408, :status => :running }
|
169
|
+
}
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
describe RedisRing::Node do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@node = RedisRing::Node.new(@slave_rpc = FakeSlaveRPC.new.connection("some_host", 666), "some_host", 666)
|
7
|
+
|
8
|
+
@slave_rpc.status = {
|
9
|
+
"joined" => true,
|
10
|
+
"running_shards" => [1, 2, 3],
|
11
|
+
"available_shards" => { "1" => 1233, "2" => 4566 }
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should return some sane defults when calling without update_status!" do
|
16
|
+
@node.joined?.should be_false
|
17
|
+
@node.running_shards.should == []
|
18
|
+
@node.available_shards.should == {}
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should only update the data when update_status! is called" do
|
22
|
+
@node.update_status!
|
23
|
+
|
24
|
+
@node.joined?.should be_true
|
25
|
+
@node.running_shards.should == [1, 2, 3]
|
26
|
+
@node.available_shards.should == {1 => 1233, 2 => 4566}
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should reflect start_shard in running shards without calling update_status!" do
|
30
|
+
@node.start_shard(9)
|
31
|
+
|
32
|
+
@node.running_shards.should == [9]
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should send rpc to slave on start_shard" do
|
36
|
+
@node.start_shard(9)
|
37
|
+
|
38
|
+
@slave_rpc.started_shards.should == [9]
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should reflect stop_shard in running shards without calling update_status!" do
|
42
|
+
@node.update_status!
|
43
|
+
@node.stop_shard(3)
|
44
|
+
|
45
|
+
@node.running_shards.should == [1, 2]
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should send rpc to slave on start_shard" do
|
49
|
+
@node.stop_shard(9)
|
50
|
+
|
51
|
+
@slave_rpc.stopped_shards.should == [9]
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
describe RedisRing::SlaveRPC do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@http_client = FakeHttpClient.new
|
7
|
+
@host = "example.com"
|
8
|
+
@port = 666
|
9
|
+
@rpc = RedisRing::SlaveRPC.new(@http_client).connection(@host, @port)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe :join do
|
13
|
+
it "should post to joined url" do
|
14
|
+
@rpc.join
|
15
|
+
|
16
|
+
@http_client.sent_post?("http://example.com:666/slave/join").should be_true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe :status do
|
21
|
+
it "should get and parse status" do
|
22
|
+
@http_client.set_response("http://example.com:666/slave/status", {"parsed" => "yes"}.to_json)
|
23
|
+
result = @rpc.status
|
24
|
+
|
25
|
+
@http_client.sent_get?("http://example.com:666/slave/status").should be_true
|
26
|
+
result.should == {"parsed" => "yes"}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe :start_shard do
|
31
|
+
it "should get and parse status" do
|
32
|
+
@rpc.start_shard(1)
|
33
|
+
|
34
|
+
@http_client.sent_post?("http://example.com:666/slave/start_shard/1").should be_true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe :stop_shard do
|
39
|
+
it "should get and parse status" do
|
40
|
+
@rpc.stop_shard(1)
|
41
|
+
|
42
|
+
@http_client.sent_post?("http://example.com:666/slave/stop_shard/1").should be_true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
describe RedisRing::Slave do
|
4
|
+
before(:each) do
|
5
|
+
@slave = RedisRing::Slave.new(@configuration = RedisRing::Configuration.new,
|
6
|
+
@master_rpc = FakeMasterRPC.new,
|
7
|
+
@process_manager = FakeProcessManager.new)
|
8
|
+
@slave.node_id = "some-node"
|
9
|
+
@slave.current_master_host = "localhost"
|
10
|
+
@slave.current_master_port = 6400
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should not be joined initially" do
|
14
|
+
@slave.should_not be_joined
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should be joined after joining" do
|
18
|
+
@slave.join
|
19
|
+
@slave.should be_joined
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should tell master to load this node on joining" do
|
23
|
+
@slave.join
|
24
|
+
|
25
|
+
@master_rpc.connection(@slave.current_master_host, @slave.current_master_port).nodes_loaded.should == [@slave.node_id]
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should start shard when asked to" do
|
29
|
+
@slave.start_shard(0)
|
30
|
+
|
31
|
+
@process_manager.started_shards.map(&:shard_number).should == [0]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should not start shard if already started" do
|
35
|
+
@slave.start_shard(0)
|
36
|
+
@slave.start_shard(0)
|
37
|
+
|
38
|
+
@process_manager.started_shards.map(&:shard_number).should == [0]
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should add shard to running when started" do
|
42
|
+
@slave.start_shard(0)
|
43
|
+
@slave.start_shard(2)
|
44
|
+
@slave.start_shard(4)
|
45
|
+
|
46
|
+
@slave.running_shards.keys.sort.should == [0, 2, 4]
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should stop shard if it is running" do
|
50
|
+
@slave.start_shard(0)
|
51
|
+
@slave.stop_shard(0)
|
52
|
+
|
53
|
+
@process_manager.stopped_shards.map(&:shard_number).should == [0]
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should do nothing if requested to stop a shard that is not running" do
|
57
|
+
@slave.stop_shard(0)
|
58
|
+
|
59
|
+
@process_manager.stopped_shards.should be_empty
|
60
|
+
end
|
61
|
+
|
62
|
+
it "it should remove stopped shard from running shards" do
|
63
|
+
@slave.start_shard(0)
|
64
|
+
@slave.start_shard(1)
|
65
|
+
@slave.stop_shard(0)
|
66
|
+
|
67
|
+
@slave.running_shards.keys.sort.should == [1]
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should return status" do
|
71
|
+
5.times { |n| @slave.start_shard(n) }
|
72
|
+
@slave.join
|
73
|
+
|
74
|
+
status = @slave.status
|
75
|
+
|
76
|
+
status[:joined].should be_true
|
77
|
+
status[:running_shards].should == (0..4).to_a
|
78
|
+
status.key?(:available_shards).should be_true
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,7 +1,26 @@
|
|
1
|
-
$:.push File.expand_path("
|
1
|
+
$:.push File.expand_path("../../lib", __FILE__)
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
require 'mocha'
|
5
|
+
|
6
|
+
require 'simplecov'
|
7
|
+
SimpleCov.start
|
2
8
|
|
3
9
|
require 'redis_ring'
|
4
10
|
|
11
|
+
def require_fake(name)
|
12
|
+
require File.expand_path("../fakes/fake_#{name}", __FILE__)
|
13
|
+
end
|
14
|
+
|
15
|
+
require_fake 'master_rpc'
|
16
|
+
require_fake 'slave_rpc'
|
17
|
+
require_fake 'process_manager'
|
18
|
+
require_fake 'zookeeper_connection'
|
19
|
+
require_fake 'node_provider'
|
20
|
+
require_fake 'http_client'
|
21
|
+
|
22
|
+
require File.expand_path('../cluster_builder', __FILE__)
|
23
|
+
|
5
24
|
RSpec.configure do |c|
|
6
25
|
c.color_enabled = true
|
7
26
|
c.mock_with :mocha
|
data/spec/test.conf
ADDED
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
+
- 1
|
7
8
|
- 0
|
8
|
-
|
9
|
-
version: 0.0.2
|
9
|
+
version: 0.1.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Adam Pohorecki
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-03-
|
17
|
+
date: 2011-03-21 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -57,7 +57,7 @@ dependencies:
|
|
57
57
|
type: :runtime
|
58
58
|
version_requirements: *id003
|
59
59
|
- !ruby/object:Gem::Dependency
|
60
|
-
name:
|
60
|
+
name: zookeeper
|
61
61
|
prerelease: false
|
62
62
|
requirement: &id004 !ruby/object:Gem::Requirement
|
63
63
|
none: false
|
@@ -67,10 +67,10 @@ dependencies:
|
|
67
67
|
segments:
|
68
68
|
- 0
|
69
69
|
version: "0"
|
70
|
-
type: :
|
70
|
+
type: :runtime
|
71
71
|
version_requirements: *id004
|
72
72
|
- !ruby/object:Gem::Dependency
|
73
|
-
name:
|
73
|
+
name: rspec
|
74
74
|
prerelease: false
|
75
75
|
requirement: &id005 !ruby/object:Gem::Requirement
|
76
76
|
none: false
|
@@ -82,6 +82,32 @@ dependencies:
|
|
82
82
|
version: "0"
|
83
83
|
type: :development
|
84
84
|
version_requirements: *id005
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: mocha
|
87
|
+
prerelease: false
|
88
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
segments:
|
94
|
+
- 0
|
95
|
+
version: "0"
|
96
|
+
type: :development
|
97
|
+
version_requirements: *id006
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: simplecov
|
100
|
+
prerelease: false
|
101
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
segments:
|
107
|
+
- 0
|
108
|
+
version: "0"
|
109
|
+
type: :development
|
110
|
+
version_requirements: *id007
|
85
111
|
description: RedisRing is a solution to run multiple small Redis instances intead of a single large one.
|
86
112
|
email:
|
87
113
|
- adam@pohorecki.pl
|
@@ -103,19 +129,41 @@ files:
|
|
103
129
|
- lib/monkey_patches.rb
|
104
130
|
- lib/redis_ring.rb
|
105
131
|
- lib/redis_ring/application.rb
|
132
|
+
- lib/redis_ring/background_thread.rb
|
106
133
|
- lib/redis_ring/cli.rb
|
107
134
|
- lib/redis_ring/configuration.rb
|
135
|
+
- lib/redis_ring/http_client.rb
|
136
|
+
- lib/redis_ring/master.rb
|
137
|
+
- lib/redis_ring/master_rpc.rb
|
138
|
+
- lib/redis_ring/node.rb
|
108
139
|
- lib/redis_ring/process_manager.rb
|
109
140
|
- lib/redis_ring/shard.rb
|
110
141
|
- lib/redis_ring/shard_config.rb
|
142
|
+
- lib/redis_ring/slave.rb
|
143
|
+
- lib/redis_ring/slave_rpc.rb
|
111
144
|
- lib/redis_ring/version.rb
|
112
145
|
- lib/redis_ring/web_interface.rb
|
146
|
+
- lib/redis_ring/zookeeper_connection.rb
|
147
|
+
- lib/redis_ring/zookeeper_observer.rb
|
113
148
|
- redis_ring.gemspec
|
149
|
+
- spec/cluster_builder.rb
|
150
|
+
- spec/fakes/fake_http_client.rb
|
151
|
+
- spec/fakes/fake_master_rpc.rb
|
152
|
+
- spec/fakes/fake_node_provider.rb
|
153
|
+
- spec/fakes/fake_process_manager.rb
|
154
|
+
- spec/fakes/fake_slave_rpc.rb
|
155
|
+
- spec/fakes/fake_zookeeper_connection.rb
|
114
156
|
- spec/redis_ring/application_spec.rb
|
115
157
|
- spec/redis_ring/configuration_spec.rb
|
158
|
+
- spec/redis_ring/master_rpc_spec.rb
|
159
|
+
- spec/redis_ring/master_spec.rb
|
160
|
+
- spec/redis_ring/node_spec.rb
|
116
161
|
- spec/redis_ring/shard_config_spec.rb
|
117
162
|
- spec/redis_ring/shard_spec.rb
|
163
|
+
- spec/redis_ring/slave_rpc_spec.rb
|
164
|
+
- spec/redis_ring/slave_spec.rb
|
118
165
|
- spec/spec_helper.rb
|
166
|
+
- spec/test.conf
|
119
167
|
has_rdoc: true
|
120
168
|
homepage: http://github.com/psyho/redis_ring
|
121
169
|
licenses: []
|