redis_ring 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,42 @@
|
|
1
|
+
module RedisRing
|
2
|
+
class SlaveRPC
|
3
|
+
|
4
|
+
attr_reader :http_client
|
5
|
+
|
6
|
+
def initialize(http_client)
|
7
|
+
@http_client = http_client
|
8
|
+
end
|
9
|
+
|
10
|
+
def connection(host, port)
|
11
|
+
Connection.new(http_client, host, port)
|
12
|
+
end
|
13
|
+
|
14
|
+
class Connection
|
15
|
+
|
16
|
+
attr_reader :http_client, :host, :port
|
17
|
+
|
18
|
+
def initialize(http_client, host, port)
|
19
|
+
@http_client = http_client
|
20
|
+
@host = host
|
21
|
+
@port = port
|
22
|
+
end
|
23
|
+
|
24
|
+
def join
|
25
|
+
http_client.post(host, port, "/slave/join")
|
26
|
+
end
|
27
|
+
|
28
|
+
def status
|
29
|
+
JSON.parse(http_client.get(host, port, "/slave/status"))
|
30
|
+
end
|
31
|
+
|
32
|
+
def start_shard(shard_no)
|
33
|
+
http_client.post(host, port, "/slave/start_shard/#{shard_no}")
|
34
|
+
end
|
35
|
+
|
36
|
+
def stop_shard(shard_no)
|
37
|
+
http_client.post(host, port, "/slave/stop_shard/#{shard_no}")
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/redis_ring/version.rb
CHANGED
@@ -1,14 +1,75 @@
|
|
1
1
|
module RedisRing
|
2
2
|
|
3
|
+
class WebInterfaceRunner
|
4
|
+
|
5
|
+
include RedisRing::BackgroundThread
|
6
|
+
|
7
|
+
attr_reader :master, :slave
|
8
|
+
|
9
|
+
def initialize(port, master, slave)
|
10
|
+
@port = port
|
11
|
+
@master = master
|
12
|
+
@slave = slave
|
13
|
+
end
|
14
|
+
|
15
|
+
def do_work
|
16
|
+
handler = Rack::Handler.get("webrick")
|
17
|
+
handler.run(WebInterface, :Port => @port, :master => @master, :slave => @slave) do |server|
|
18
|
+
@server = server
|
19
|
+
WebInterface.set :master, master
|
20
|
+
WebInterface.set :slave, slave
|
21
|
+
WebInterface.set :running, true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def halt
|
26
|
+
super
|
27
|
+
@server.stop if @server
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
3
32
|
class WebInterface < Sinatra::Base
|
4
33
|
|
34
|
+
def master
|
35
|
+
self.class.master
|
36
|
+
end
|
37
|
+
|
38
|
+
def slave
|
39
|
+
self.class.slave
|
40
|
+
end
|
41
|
+
|
5
42
|
get "/" do
|
6
43
|
"RedisRing is running"
|
7
44
|
end
|
8
45
|
|
9
|
-
|
46
|
+
post "/master/node_joined/:node_id" do
|
47
|
+
master.node_joined(params[:node_id])
|
48
|
+
"OK"
|
49
|
+
end
|
50
|
+
|
51
|
+
get "/slave/status" do
|
10
52
|
content_type :json
|
11
|
-
|
53
|
+
slave.status.to_json
|
54
|
+
end
|
55
|
+
|
56
|
+
post "/slave/join" do
|
57
|
+
slave.join
|
58
|
+
"OK"
|
59
|
+
end
|
60
|
+
|
61
|
+
post "/slave/start_shard/:shard_no" do
|
62
|
+
slave.start_shard(params[:shard_no].to_i)
|
63
|
+
"OK"
|
64
|
+
end
|
65
|
+
|
66
|
+
post "/slave/stop_shard/:shard_no" do
|
67
|
+
slave.stop_shard(params[:shard_no].to_i)
|
68
|
+
"OK"
|
69
|
+
end
|
70
|
+
|
71
|
+
class << self
|
72
|
+
attr_accessor :master, :slave
|
12
73
|
end
|
13
74
|
|
14
75
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module RedisRing
|
2
|
+
|
3
|
+
class ZookeeperConnection
|
4
|
+
|
5
|
+
attr_reader :current_node
|
6
|
+
|
7
|
+
def initialize(host_name, base_port, zookeeper_address)
|
8
|
+
@host_name = host_name
|
9
|
+
@base_port = base_port
|
10
|
+
@zookeeper_address = zookeeper_address
|
11
|
+
@connected = false
|
12
|
+
@base_path = "/nodes"
|
13
|
+
@mutex = Mutex.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def nodes_changed?
|
17
|
+
return true unless nodes_watcher
|
18
|
+
return nodes_watcher.completed?
|
19
|
+
end
|
20
|
+
|
21
|
+
def nodes
|
22
|
+
@nodes_watcher = Zookeeper::WatcherCallback.new
|
23
|
+
resp = zookeeper.get_children(:path => base_path, :watcher => nodes_watcher, :watcher_context => base_path)
|
24
|
+
return resp[:children].sort
|
25
|
+
end
|
26
|
+
|
27
|
+
def node_data(node)
|
28
|
+
resp = zookeeper.get(:path => "#{base_path}/#{node}")
|
29
|
+
data = resp[:data]
|
30
|
+
return data ? JSON.parse(data) : nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def update_status(status)
|
34
|
+
zookeeper.set(:path => "#{base_path}/status", :data => status.to_json)
|
35
|
+
end
|
36
|
+
|
37
|
+
def connected?
|
38
|
+
@connected
|
39
|
+
end
|
40
|
+
|
41
|
+
def connect
|
42
|
+
@mutex.synchronize do
|
43
|
+
break if connected?
|
44
|
+
|
45
|
+
@zookeeper = Zookeeper.new(zookeeper_address)
|
46
|
+
|
47
|
+
if @zookeeper.state != Zookeeper::ZOO_CONNECTED_STATE
|
48
|
+
raise "Zookeeper not connected!"
|
49
|
+
end
|
50
|
+
|
51
|
+
resp = @zookeeper.create(:path => base_path)
|
52
|
+
#raise "Could not create base path" unless resp[:rc] == Zookeeper::ZOK
|
53
|
+
|
54
|
+
resp = @zookeeper.create(:path => "#{base_path}/node-", :ephemeral => true, :sequence => true, :data => current_node_data.to_json)
|
55
|
+
#raise "Could not create node" unless resp[:rc] == Zookeeper::ZOK
|
56
|
+
|
57
|
+
@current_node = resp[:path].gsub("#{base_path}/", '')
|
58
|
+
|
59
|
+
@connected = true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
attr_reader :zookeeper, :base_path, :nodes_watcher, :zookeeper_address, :host_name, :base_port
|
66
|
+
|
67
|
+
def current_node_data
|
68
|
+
{:host => host_name, :port => base_port}
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module RedisRing
|
2
|
+
|
3
|
+
class ZookeeperObserver
|
4
|
+
|
5
|
+
include RedisRing::BackgroundThread
|
6
|
+
|
7
|
+
attr_reader :master, :slave, :zookeeper_connection
|
8
|
+
|
9
|
+
def initialize(zookeeper_connection, master, slave)
|
10
|
+
@zookeeper_connection = zookeeper_connection
|
11
|
+
@master = master
|
12
|
+
@slave = slave
|
13
|
+
@current_master = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def do_work
|
17
|
+
on_node_list_changed(zookeeper_connection.nodes) if zookeeper_connection.nodes_changed?
|
18
|
+
sleep(0.1)
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def on_node_list_changed(new_nodes)
|
24
|
+
current_master = new_nodes.first
|
25
|
+
|
26
|
+
unless @current_master == current_master
|
27
|
+
@current_master = current_master
|
28
|
+
|
29
|
+
if current_master == zookeeper_connection.current_node
|
30
|
+
master.became_master
|
31
|
+
else
|
32
|
+
master.no_longer_is_master
|
33
|
+
end
|
34
|
+
|
35
|
+
current_master_data = zookeeper_connection.node_data(current_master)
|
36
|
+
slave.current_master_host = current_master_data["host"]
|
37
|
+
slave.current_master_port = current_master_data["port"]
|
38
|
+
|
39
|
+
puts "NEW MASTER IS: #{slave.current_master_host}:#{slave.current_master_port}"
|
40
|
+
end
|
41
|
+
|
42
|
+
master.nodes_changed(new_nodes)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/lib/redis_ring.rb
CHANGED
@@ -2,17 +2,28 @@ require 'socket'
|
|
2
2
|
require 'yaml'
|
3
3
|
require 'erb'
|
4
4
|
require 'fileutils'
|
5
|
+
require 'net/http'
|
5
6
|
|
6
7
|
require 'sinatra'
|
7
8
|
require 'json'
|
8
9
|
require 'daemons'
|
10
|
+
require 'zookeeper'
|
9
11
|
|
10
12
|
require 'monkey_patches'
|
11
13
|
|
14
|
+
require 'redis_ring/background_thread'
|
12
15
|
require 'redis_ring/configuration'
|
13
16
|
require 'redis_ring/shard_config'
|
14
17
|
require 'redis_ring/shard'
|
18
|
+
require 'redis_ring/http_client'
|
19
|
+
require 'redis_ring/node'
|
15
20
|
require 'redis_ring/application'
|
16
21
|
require 'redis_ring/web_interface'
|
17
22
|
require 'redis_ring/process_manager'
|
23
|
+
require 'redis_ring/master'
|
24
|
+
require 'redis_ring/master_rpc'
|
25
|
+
require 'redis_ring/slave'
|
26
|
+
require 'redis_ring/slave_rpc'
|
27
|
+
require 'redis_ring/zookeeper_observer'
|
28
|
+
require 'redis_ring/zookeeper_connection'
|
18
29
|
require 'redis_ring/cli'
|
data/redis_ring.gemspec
CHANGED
@@ -17,9 +17,11 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.add_dependency 'sinatra'
|
18
18
|
s.add_dependency 'json'
|
19
19
|
s.add_dependency 'daemons-mikehale'
|
20
|
+
s.add_dependency 'zookeeper'
|
20
21
|
|
21
22
|
s.add_development_dependency 'rspec'
|
22
23
|
s.add_development_dependency 'mocha'
|
24
|
+
s.add_development_dependency 'simplecov'
|
23
25
|
|
24
26
|
s.files = `git ls-files`.split("\n")
|
25
27
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -0,0 +1,224 @@
|
|
1
|
+
class ClusterBuilder
|
2
|
+
|
3
|
+
def initialize
|
4
|
+
yield self
|
5
|
+
end
|
6
|
+
|
7
|
+
def nodes
|
8
|
+
@nodes ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def node(node_id)
|
12
|
+
nodes[node_id] ||= NodeBuilder.new(node_id, self)
|
13
|
+
end
|
14
|
+
|
15
|
+
def node_ids
|
16
|
+
return nodes.keys.sort
|
17
|
+
end
|
18
|
+
|
19
|
+
def start_shard(node_id, shard_number)
|
20
|
+
start_shard_callbacks.each do |block|
|
21
|
+
block.call(node_id, shard_number)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def stop_shard(node_id, shard_number)
|
26
|
+
stop_shard_callbacks.each do |block|
|
27
|
+
block.call(node_id, shard_number)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_start_shard(&block)
|
32
|
+
start_shard_callbacks << block
|
33
|
+
end
|
34
|
+
|
35
|
+
def on_stop_shard(&block)
|
36
|
+
stop_shard_callbacks << block
|
37
|
+
end
|
38
|
+
|
39
|
+
def start_shard_callbacks
|
40
|
+
@start_shard_callbacks ||= []
|
41
|
+
end
|
42
|
+
|
43
|
+
def stop_shard_callbacks
|
44
|
+
@stop_shard_callbacks ||= []
|
45
|
+
end
|
46
|
+
|
47
|
+
def fake_provider
|
48
|
+
@fake_provider ||= FakeNodeProvider.new(self)
|
49
|
+
end
|
50
|
+
|
51
|
+
def fake_connection
|
52
|
+
@fake_connection ||= FakeConnection.new(self)
|
53
|
+
end
|
54
|
+
|
55
|
+
class FakeNodeProvider
|
56
|
+
def initialize(cluster_builder)
|
57
|
+
@cluster_builder = cluster_builder
|
58
|
+
@count = 0
|
59
|
+
end
|
60
|
+
|
61
|
+
def new(host, port)
|
62
|
+
result = @cluster_builder.nodes.detect{|_, node| node.get.host == host && node.get.port == port }
|
63
|
+
return result[1].get if result
|
64
|
+
return @cluster_builder.node("unknown-node-#{@count += 1}").host(host).port(port).reachable(false).get
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class FakeConnection
|
69
|
+
def initialize(cluster_builder)
|
70
|
+
@cluster_builder = cluster_builder
|
71
|
+
end
|
72
|
+
|
73
|
+
def node_data(node_id)
|
74
|
+
return nil unless @cluster_builder.nodes.key?(node_id)
|
75
|
+
node = @cluster_builder.node(node_id).get
|
76
|
+
return {"host" => node.host, "port" => node.port}
|
77
|
+
end
|
78
|
+
|
79
|
+
def update_status(status)
|
80
|
+
@status = status
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class NodeBuilder
|
85
|
+
def initialize(node_id, cluster_builder)
|
86
|
+
@node_id = node_id
|
87
|
+
@joined = true
|
88
|
+
@running_shards = []
|
89
|
+
@available_shards = {}
|
90
|
+
@host = "localhost"
|
91
|
+
@port = 6400
|
92
|
+
@reachable = true
|
93
|
+
@cluster_builder = cluster_builder
|
94
|
+
end
|
95
|
+
|
96
|
+
def host(str)
|
97
|
+
@host = str
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
def port(int)
|
102
|
+
@port = int
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
def reachable(bool)
|
107
|
+
@reachable = bool
|
108
|
+
self
|
109
|
+
end
|
110
|
+
|
111
|
+
def joined(bool)
|
112
|
+
@joined = bool
|
113
|
+
self
|
114
|
+
end
|
115
|
+
|
116
|
+
def running_shards(arr)
|
117
|
+
@running_shards = arr
|
118
|
+
self
|
119
|
+
end
|
120
|
+
|
121
|
+
def running_shard(shard)
|
122
|
+
@running_shards << shard unless @running_shards.include?(shard)
|
123
|
+
self
|
124
|
+
end
|
125
|
+
|
126
|
+
def not_running_shard(shard)
|
127
|
+
@running_shards.delete(shard)
|
128
|
+
self
|
129
|
+
end
|
130
|
+
|
131
|
+
def available_shards(hash)
|
132
|
+
@available_shards = hash
|
133
|
+
self
|
134
|
+
end
|
135
|
+
|
136
|
+
def available_shard(shard, timestamp)
|
137
|
+
@available_shards[shard] = timestamp
|
138
|
+
self
|
139
|
+
end
|
140
|
+
|
141
|
+
def not_available_shard(shard)
|
142
|
+
@available_shards.delete(shard)
|
143
|
+
self
|
144
|
+
end
|
145
|
+
|
146
|
+
def load_properties(node)
|
147
|
+
node.load(props)
|
148
|
+
end
|
149
|
+
|
150
|
+
def props
|
151
|
+
{
|
152
|
+
:host => @host,
|
153
|
+
:port => @port,
|
154
|
+
:node_id => @node_id,
|
155
|
+
:running_shards => @running_shards,
|
156
|
+
:available_shards => @available_shards,
|
157
|
+
:joined => @joined,
|
158
|
+
:reachable => @reachable,
|
159
|
+
:cluster_builder => @cluster_builder
|
160
|
+
}
|
161
|
+
end
|
162
|
+
|
163
|
+
def get
|
164
|
+
@node ||= FakeNode.new(self)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
class FakeNode
|
169
|
+
|
170
|
+
attr_reader :host, :port, :node_id
|
171
|
+
|
172
|
+
def initialize(builder)
|
173
|
+
@builder = builder
|
174
|
+
@builder.load_properties(self)
|
175
|
+
end
|
176
|
+
|
177
|
+
def load(opts)
|
178
|
+
@joined = opts[:joined]
|
179
|
+
@running_shards = opts[:running_shards]
|
180
|
+
@available_shards = opts[:available_shards]
|
181
|
+
@reachable = opts[:reachable]
|
182
|
+
@host = opts[:host]
|
183
|
+
@port = opts[:port]
|
184
|
+
@node_id = opts[:node_id]
|
185
|
+
@cluster_builder = opts[:cluster_builder]
|
186
|
+
end
|
187
|
+
|
188
|
+
def joined?
|
189
|
+
@joined
|
190
|
+
end
|
191
|
+
|
192
|
+
def available_shards
|
193
|
+
@available_shards
|
194
|
+
end
|
195
|
+
|
196
|
+
def running_shards
|
197
|
+
@running_shards
|
198
|
+
end
|
199
|
+
|
200
|
+
def start_shard(shard_number)
|
201
|
+
ensure_reachable
|
202
|
+
@running_shards << shard_number unless @running_shards.include?(shard_number)
|
203
|
+
@cluster_builder.start_shard(@node_id, shard_number)
|
204
|
+
end
|
205
|
+
|
206
|
+
def stop_shard(shard_number)
|
207
|
+
ensure_reachable
|
208
|
+
@running_shards.delete(shard_number)
|
209
|
+
@cluster_builder.stop_shard(@node_id, shard_number)
|
210
|
+
end
|
211
|
+
|
212
|
+
def update_status!
|
213
|
+
@builder.load_properties(self)
|
214
|
+
ensure_reachable
|
215
|
+
end
|
216
|
+
|
217
|
+
protected
|
218
|
+
|
219
|
+
def ensure_reachable
|
220
|
+
raise "Node #{@node_id} #{@host}:#{@port} is not reachable!" unless @reachable
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class FakeHttpClient < RedisRing::HttpClient
|
2
|
+
|
3
|
+
def posts
|
4
|
+
@posts ||= []
|
5
|
+
end
|
6
|
+
|
7
|
+
def gets
|
8
|
+
@gets ||= []
|
9
|
+
end
|
10
|
+
|
11
|
+
def responses
|
12
|
+
@responses ||= Hash.new("OK")
|
13
|
+
end
|
14
|
+
|
15
|
+
def post(host, port, path, params = {})
|
16
|
+
url = uri(host, port, path, params).to_s
|
17
|
+
posts << url
|
18
|
+
return responses[url]
|
19
|
+
end
|
20
|
+
|
21
|
+
def get(host, port, path, params = {})
|
22
|
+
url = uri(host, port, path, params).to_s
|
23
|
+
gets << url
|
24
|
+
return responses[url]
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_response(url, text)
|
28
|
+
responses[url] = text
|
29
|
+
end
|
30
|
+
|
31
|
+
def sent_post?(url)
|
32
|
+
posts.include?(url)
|
33
|
+
end
|
34
|
+
|
35
|
+
def sent_get?(url)
|
36
|
+
gets.include?(url)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class FakeMasterRPC
|
2
|
+
|
3
|
+
def connection(host, port)
|
4
|
+
@connections ||= {}
|
5
|
+
@connections["#{host}:#{port}"] ||= Connection.new
|
6
|
+
end
|
7
|
+
|
8
|
+
class Connection
|
9
|
+
|
10
|
+
def nodes_loaded
|
11
|
+
@node_loaded ||= []
|
12
|
+
end
|
13
|
+
|
14
|
+
def node_loaded(node_id)
|
15
|
+
nodes_loaded << node_id
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class FakeProcessManager
|
2
|
+
|
3
|
+
def started_shards
|
4
|
+
@started_shards ||= []
|
5
|
+
end
|
6
|
+
|
7
|
+
def start_shard(shard)
|
8
|
+
started_shards << shard
|
9
|
+
end
|
10
|
+
|
11
|
+
def stopped_shards
|
12
|
+
@stopped_shards ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
def stop_shard(shard)
|
16
|
+
stopped_shards << shard
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class FakeSlaveRPC
|
2
|
+
|
3
|
+
def connection(host, port)
|
4
|
+
@connections ||= {}
|
5
|
+
@connections["#{host}:#{port}"] ||= Connection.new
|
6
|
+
end
|
7
|
+
|
8
|
+
class Connection
|
9
|
+
|
10
|
+
def status=(val)
|
11
|
+
@status = val
|
12
|
+
end
|
13
|
+
|
14
|
+
def status
|
15
|
+
@status ||= {"joined" => false, "running_shards" => [], "available_shards" => {}}
|
16
|
+
end
|
17
|
+
|
18
|
+
def started_shards
|
19
|
+
@started_shards ||= []
|
20
|
+
end
|
21
|
+
|
22
|
+
def start_shard(shard_number)
|
23
|
+
started_shards << shard_number
|
24
|
+
end
|
25
|
+
|
26
|
+
def stopped_shards
|
27
|
+
@stopped_shards ||= []
|
28
|
+
end
|
29
|
+
|
30
|
+
def stop_shard(shard_number)
|
31
|
+
stopped_shards << shard_number
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -2,22 +2,24 @@ require File.expand_path("../../spec_helper", __FILE__)
|
|
2
2
|
|
3
3
|
describe RedisRing::Application do
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
before(:each) do
|
6
|
+
@conf = RedisRing::Configuration.from_yml_file(File.expand_path("../../test.conf", __FILE__))
|
7
|
+
@app = RedisRing::Application.new(@conf)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should run shards" do
|
11
|
+
@app.start
|
12
|
+
sleep(1)
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
end
|
14
|
+
@app.slave.join
|
15
|
+
sleep(3)
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
+
@app.slave.running_shards.keys.sort.should == [0, 1, 2, 3]
|
18
|
+
end
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
-
|
20
|
+
after(:each) do
|
21
|
+
@app.stop
|
22
|
+
@app.wait
|
21
23
|
end
|
22
24
|
|
23
25
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
describe RedisRing::MasterRPC do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@http_client = FakeHttpClient.new
|
7
|
+
@host = "example.com"
|
8
|
+
@port = 666
|
9
|
+
@rpc = RedisRing::MasterRPC.new(@http_client).connection(@host, @port)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe :node_joined do
|
13
|
+
it "should post to node joined url" do
|
14
|
+
@rpc.node_loaded("some_node_id")
|
15
|
+
|
16
|
+
@http_client.sent_post?("http://example.com:666/master/node_joined/some_node_id").should be_true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|