beetle 0.1 → 0.2.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/README.rdoc +18 -8
- data/beetle.gemspec +37 -121
- data/bin/beetle +9 -0
- data/examples/README.rdoc +0 -2
- data/examples/rpc.rb +3 -2
- data/ext/mkrf_conf.rb +19 -0
- data/lib/beetle/base.rb +1 -8
- data/lib/beetle/client.rb +16 -14
- data/lib/beetle/commands/configuration_client.rb +73 -0
- data/lib/beetle/commands/configuration_server.rb +85 -0
- data/lib/beetle/commands.rb +30 -0
- data/lib/beetle/configuration.rb +70 -7
- data/lib/beetle/deduplication_store.rb +50 -38
- data/lib/beetle/handler.rb +2 -5
- data/lib/beetle/logging.rb +7 -0
- data/lib/beetle/message.rb +11 -13
- data/lib/beetle/publisher.rb +2 -2
- data/lib/beetle/r_c.rb +2 -1
- data/lib/beetle/redis_configuration_client.rb +136 -0
- data/lib/beetle/redis_configuration_server.rb +301 -0
- data/lib/beetle/redis_ext.rb +79 -0
- data/lib/beetle/redis_master_file.rb +35 -0
- data/lib/beetle/redis_server_info.rb +65 -0
- data/lib/beetle/subscriber.rb +4 -1
- data/lib/beetle.rb +2 -2
- data/test/beetle/configuration_test.rb +14 -2
- data/test/beetle/deduplication_store_test.rb +61 -43
- data/test/beetle/message_test.rb +28 -4
- data/test/beetle/redis_configuration_client_test.rb +97 -0
- data/test/beetle/redis_configuration_server_test.rb +278 -0
- data/test/beetle/redis_ext_test.rb +71 -0
- data/test/beetle/redis_master_file_test.rb +39 -0
- data/test/test_helper.rb +13 -1
- metadata +59 -50
- data/.gitignore +0 -5
- data/MIT-LICENSE +0 -20
- data/Rakefile +0 -114
- data/TODO +0 -7
- data/doc/redundant_queues.graffle +0 -7744
- data/etc/redis-master.conf +0 -189
- data/etc/redis-slave.conf +0 -189
- data/examples/redis_failover.rb +0 -65
- data/script/start_rabbit +0 -29
- data/snafu.rb +0 -55
- data/test/beetle/bla.rb +0 -0
- data/test/beetle.yml +0 -81
- data/tmp/master/.gitignore +0 -2
- data/tmp/slave/.gitignore +0 -3
data/test/beetle/message_test.rb
CHANGED
@@ -4,6 +4,11 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
|
4
4
|
module Beetle
|
5
5
|
|
6
6
|
class EncodingTest < Test::Unit::TestCase
|
7
|
+
test "an exception during decoding should be stored in the exception attribute" do
|
8
|
+
header = stub_everything("raising header")
|
9
|
+
m = Message.new("queue", header, 'foo')
|
10
|
+
assert_instance_of NoMethodError, m.exception
|
11
|
+
end
|
7
12
|
|
8
13
|
test "a message should encode/decode the message format version correctly" do
|
9
14
|
header = header_with_params({})
|
@@ -371,6 +376,17 @@ module Beetle
|
|
371
376
|
@store.flushdb
|
372
377
|
end
|
373
378
|
|
379
|
+
test "a message with an exception set should not be processed at all, but it should be acked" do
|
380
|
+
header = {}
|
381
|
+
message = Message.new("somequeue", header, 'foo')
|
382
|
+
assert message.exception
|
383
|
+
|
384
|
+
proc = mock("proc")
|
385
|
+
proc.expects(:call).never
|
386
|
+
message.expects(:ack!)
|
387
|
+
assert_equal RC::DecodingError, message.__send__(:process_internal, proc)
|
388
|
+
end
|
389
|
+
|
374
390
|
test "a completed existing message should be just acked and not run the handler" do
|
375
391
|
header = header_with_params({})
|
376
392
|
message = Message.new("somequeue", header, 'foo', :attempts => 2, :store => @store)
|
@@ -379,7 +395,6 @@ module Beetle
|
|
379
395
|
assert message.completed?
|
380
396
|
|
381
397
|
proc = mock("proc")
|
382
|
-
s = sequence("s")
|
383
398
|
header.expects(:ack)
|
384
399
|
proc.expects(:call).never
|
385
400
|
assert_equal RC::OK, message.__send__(:process_internal, proc)
|
@@ -394,7 +409,6 @@ module Beetle
|
|
394
409
|
assert message.delayed?
|
395
410
|
|
396
411
|
proc = mock("proc")
|
397
|
-
s = sequence("s")
|
398
412
|
header.expects(:ack).never
|
399
413
|
proc.expects(:call).never
|
400
414
|
assert_equal RC::Delayed, message.__send__(:process_internal, proc)
|
@@ -412,7 +426,6 @@ module Beetle
|
|
412
426
|
assert !message.timed_out?
|
413
427
|
|
414
428
|
proc = mock("proc")
|
415
|
-
s = sequence("s")
|
416
429
|
header.expects(:ack).never
|
417
430
|
proc.expects(:call).never
|
418
431
|
assert_equal RC::HandlerNotYetTimedOut, message.__send__(:process_internal, proc)
|
@@ -577,7 +590,7 @@ module Beetle
|
|
577
590
|
@store.flushdb
|
578
591
|
end
|
579
592
|
|
580
|
-
test "a handler running longer than the specified timeout should be aborted" do
|
593
|
+
test "a handler running longer than the specified timeout should be aborted (when given a float timeout number)" do
|
581
594
|
header = header_with_params({})
|
582
595
|
header.expects(:ack)
|
583
596
|
message = Message.new("somequeue", header, 'foo', :timeout => 0.1, :attempts => 2, :store => @store)
|
@@ -586,6 +599,17 @@ module Beetle
|
|
586
599
|
result = message.process(handler)
|
587
600
|
assert_equal RC::ExceptionsLimitReached, result
|
588
601
|
end
|
602
|
+
|
603
|
+
test "a handler running longer than the specified timeout should be aborted (when using active_support seconds)" do
|
604
|
+
header = header_with_params({})
|
605
|
+
header.expects(:ack)
|
606
|
+
message = Message.new("somequeue", header, 'foo', :timeout => 1.seconds, :attempts => 2, :store => @store)
|
607
|
+
action = lambda{|*args| while true; end}
|
608
|
+
handler = Handler.create(action)
|
609
|
+
result = message.process(handler)
|
610
|
+
assert_equal RC::ExceptionsLimitReached, result
|
611
|
+
end
|
612
|
+
|
589
613
|
end
|
590
614
|
|
591
615
|
class SettingsTest < Test::Unit::TestCase
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
|
3
|
+
module Beetle
|
4
|
+
class RedisConfigurationClientTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
Beetle.config.redis_servers = "redis:0,redis:1"
|
7
|
+
@client = RedisConfigurationClient.new
|
8
|
+
Client.any_instance.stubs(:listen)
|
9
|
+
@client.stubs(:touch_master_file)
|
10
|
+
@client.stubs(:verify_redis_master_file_string)
|
11
|
+
end
|
12
|
+
|
13
|
+
test "config should return the beetle config" do
|
14
|
+
assert_equal Beetle.config, @client.config
|
15
|
+
end
|
16
|
+
|
17
|
+
test "ping message should answer with pong" do
|
18
|
+
@client.expects(:pong!)
|
19
|
+
@client.ping("token" => 1)
|
20
|
+
end
|
21
|
+
|
22
|
+
test "pong should publish a pong message" do
|
23
|
+
@client.beetle.expects(:publish)
|
24
|
+
@client.send(:pong!)
|
25
|
+
end
|
26
|
+
|
27
|
+
test "invalidation should send an invalidation message and clear the redis master file" do
|
28
|
+
@client.expects(:clear_redis_master_file)
|
29
|
+
@client.beetle.expects(:publish).with(:client_invalidated, anything)
|
30
|
+
@client.send(:invalidate!)
|
31
|
+
end
|
32
|
+
|
33
|
+
test "should ignore outdated invalidate messages" do
|
34
|
+
new_payload = {"token" => 2}
|
35
|
+
old_payload = {"token" => 1}
|
36
|
+
|
37
|
+
@client.expects(:invalidate!).once
|
38
|
+
|
39
|
+
@client.invalidate(new_payload)
|
40
|
+
@client.invalidate(old_payload)
|
41
|
+
end
|
42
|
+
|
43
|
+
test "should ignore invalidate messages when current master is still a master" do
|
44
|
+
@client.instance_variable_set :@current_master, stub(:master? => true)
|
45
|
+
@client.expects(:invalidate!).never
|
46
|
+
@client.invalidate("token" => 1)
|
47
|
+
end
|
48
|
+
|
49
|
+
test "should ignore outdated reconfigure messages" do
|
50
|
+
new_payload = {"token" => 2, "server" => "master:2"}
|
51
|
+
old_payload = {"token" => 1, "server" => "master:1"}
|
52
|
+
@client.stubs(:read_redis_master_file).returns("")
|
53
|
+
|
54
|
+
@client.expects(:write_redis_master_file).once
|
55
|
+
|
56
|
+
@client.reconfigure(new_payload)
|
57
|
+
@client.reconfigure(old_payload)
|
58
|
+
end
|
59
|
+
|
60
|
+
test "should clear redis master file if redis from master file is slave" do
|
61
|
+
@client.stubs(:redis_master_from_master_file).returns(stub(:master? => false))
|
62
|
+
@client.expects(:clear_redis_master_file)
|
63
|
+
@client.start
|
64
|
+
end
|
65
|
+
|
66
|
+
test "should clear redis master file if redis from master file is not available" do
|
67
|
+
@client.stubs(:redis_master_from_master_file).returns(nil)
|
68
|
+
@client.expects(:clear_redis_master_file)
|
69
|
+
@client.start
|
70
|
+
end
|
71
|
+
|
72
|
+
test "the dispatcher should just forward messages to the client" do
|
73
|
+
dispatcher_class = RedisConfigurationClient.class_eval "MessageDispatcher"
|
74
|
+
dispatcher_class.configuration_client = @client
|
75
|
+
dispatcher = dispatcher_class.new
|
76
|
+
payload = {"token" => 1}
|
77
|
+
dispatcher.stubs(:message).returns(stub(:data => payload.to_json, :header => stub(:routing_key=> "ping")))
|
78
|
+
@client.expects(:ping).with(payload)
|
79
|
+
dispatcher.send(:process)
|
80
|
+
end
|
81
|
+
|
82
|
+
test "determine_initial_master should return nil if there is no file" do
|
83
|
+
@client.expects(:master_file_exists?).returns(false)
|
84
|
+
assert_nil @client.send(:determine_initial_master)
|
85
|
+
assert_nil @client.current_master
|
86
|
+
end
|
87
|
+
|
88
|
+
test "determine_initial_master should instantiate a new redis if there is a file with content" do
|
89
|
+
@client.expects(:master_file_exists?).returns(true)
|
90
|
+
@client.expects(:read_redis_master_file).returns("localhost:6379")
|
91
|
+
master = @client.send(:determine_initial_master)
|
92
|
+
assert_equal "master", master.role
|
93
|
+
assert_equal master, @client.current_master
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,278 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
|
3
|
+
module Beetle
|
4
|
+
class RedisConfigurationServerTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
Beetle.config.redis_configuration_client_ids = "rc-client-1,rc-client-2"
|
7
|
+
@server = RedisConfigurationServer.new
|
8
|
+
EventMachine.stubs(:add_timer).yields
|
9
|
+
end
|
10
|
+
|
11
|
+
test "should exit when started with less than two redis configured" do
|
12
|
+
Beetle.config.redis_servers = ""
|
13
|
+
assert_raise Beetle::ConfigurationError do
|
14
|
+
@server.start
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
test "should initialize the current token for messages to not reuse old tokens" do
|
19
|
+
sleep 0.1
|
20
|
+
later_server = RedisConfigurationServer.new
|
21
|
+
assert later_server.current_token > @server.current_token
|
22
|
+
end
|
23
|
+
|
24
|
+
test "should ignore outdated client_invalidated messages" do
|
25
|
+
@server.instance_variable_set(:@current_token, 2)
|
26
|
+
@server.client_invalidated("id" => "rc-client-1", "token" => 2)
|
27
|
+
old_token = 1.minute.ago.to_f
|
28
|
+
@server.client_invalidated("id" => "rc-client-2", "token" => 1)
|
29
|
+
|
30
|
+
assert_equal(["rc-client-1"].to_set, @server.instance_variable_get(:@client_invalidated_ids_received))
|
31
|
+
end
|
32
|
+
|
33
|
+
test "should ignore outdated pong messages" do
|
34
|
+
@server.instance_variable_set(:@current_token, 2)
|
35
|
+
@server.pong("id" => "rc-client-1", "token" => 2)
|
36
|
+
old_token = 1.minute.ago.to_f
|
37
|
+
@server.pong("id" => "rc-client-2", "token" => 1)
|
38
|
+
|
39
|
+
assert_equal(["rc-client-1"].to_set, @server.instance_variable_get(:@client_pong_ids_received))
|
40
|
+
end
|
41
|
+
|
42
|
+
test "the dispatcher should just forward messages to the server" do
|
43
|
+
dispatcher_class = RedisConfigurationServer.class_eval "MessageDispatcher"
|
44
|
+
dispatcher_class.configuration_server = @server
|
45
|
+
dispatcher = dispatcher_class.new
|
46
|
+
payload = {"token" => 1}
|
47
|
+
dispatcher.stubs(:message).returns(stub(:data => payload.to_json, :header => stub(:routing_key=> "pong")))
|
48
|
+
@server.expects(:pong).with(payload)
|
49
|
+
dispatcher.send(:process)
|
50
|
+
end
|
51
|
+
|
52
|
+
test "if a new master is available, it should be published and the available slaves should be configured" do
|
53
|
+
redis = Redis.new
|
54
|
+
other_master = Redis.new(:port => 6380)
|
55
|
+
other_master.expects(:slave_of!).with(redis.host, redis.port)
|
56
|
+
@server.stubs(:current_master).returns(redis)
|
57
|
+
@server.redis.instance_variable_set(:@server_info, {"master" => [redis, other_master], "slave" => [], "unknown" => []})
|
58
|
+
payload = @server.send(:payload_with_current_token, {"server" => redis.server})
|
59
|
+
@server.beetle.expects(:publish).with(:reconfigure, payload)
|
60
|
+
@server.master_available!
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class RedisConfigurationServerInvalidationTest < Test::Unit::TestCase
|
65
|
+
def setup
|
66
|
+
Beetle.config.redis_configuration_client_ids = "rc-client-1,rc-client-2"
|
67
|
+
Beetle.config.redis_servers = "redis:0,redis:1"
|
68
|
+
@server = RedisConfigurationServer.new
|
69
|
+
@server.instance_variable_set(:@current_master, stub('redis stub', :server => 'stubbed_server', :available? => false))
|
70
|
+
@server.stubs(:verify_redis_master_file_string)
|
71
|
+
@server.beetle.stubs(:listen).yields
|
72
|
+
@server.beetle.stubs(:publish)
|
73
|
+
EM::Timer.stubs(:new).returns(true)
|
74
|
+
EventMachine.stubs(:add_periodic_timer).yields
|
75
|
+
end
|
76
|
+
|
77
|
+
test "should pause watching of the redis master when it becomes unavailable" do
|
78
|
+
@server.expects(:determine_initial_master)
|
79
|
+
EM.stubs(:add_periodic_timer).returns(stub("timer", :cancel => true))
|
80
|
+
@server.start
|
81
|
+
assert !@server.paused?
|
82
|
+
@server.master_unavailable!
|
83
|
+
assert @server.paused?
|
84
|
+
end
|
85
|
+
|
86
|
+
test "should setup an invalidation timeout" do
|
87
|
+
EM::Timer.expects(:new).yields
|
88
|
+
@server.expects(:cancel_invalidation)
|
89
|
+
@server.master_unavailable!
|
90
|
+
end
|
91
|
+
|
92
|
+
test "should continue watching after the invalidation timeout has expired" do
|
93
|
+
EM::Timer.expects(:new).yields
|
94
|
+
@server.master_unavailable!
|
95
|
+
assert !@server.paused?
|
96
|
+
end
|
97
|
+
|
98
|
+
test "should invalidate the current master after receiving all pong messages" do
|
99
|
+
EM::Timer.expects(:new).yields.returns(:timer)
|
100
|
+
@server.beetle.expects(:publish).with(:invalidate, anything)
|
101
|
+
@server.expects(:cancel_invalidation)
|
102
|
+
@server.expects(:redeem_token).with(1).twice.returns(true)
|
103
|
+
@server.pong("token" => 1, "id" => "rc-client-1")
|
104
|
+
@server.pong("token" => 1, "id" => "rc-client-2")
|
105
|
+
end
|
106
|
+
|
107
|
+
test "should switch the current master after receiving all client_invalidated messages" do
|
108
|
+
@server.expects(:redeem_token).with(1).twice.returns(true)
|
109
|
+
@server.expects(:switch_master)
|
110
|
+
@server.client_invalidated("token" => 1, "id" => "rc-client-1")
|
111
|
+
@server.client_invalidated("token" => 1, "id" => "rc-client-2")
|
112
|
+
end
|
113
|
+
|
114
|
+
test "should switch the current master immediately if there are no clients" do
|
115
|
+
@server.instance_variable_set :@client_ids, Set.new
|
116
|
+
@server.expects(:switch_master)
|
117
|
+
@server.master_unavailable!
|
118
|
+
end
|
119
|
+
|
120
|
+
test "switching the master should turn the new master candidate into a master" do
|
121
|
+
new_master = stub(:master! => nil, :server => "jo:6379")
|
122
|
+
@server.beetle.expects(:publish).with(:system_notification, anything)
|
123
|
+
@server.expects(:determine_new_master).returns(new_master)
|
124
|
+
@server.send :switch_master
|
125
|
+
assert_equal new_master, @server.current_master
|
126
|
+
end
|
127
|
+
|
128
|
+
test "switching the master should resort to the old master if no candidate can be found" do
|
129
|
+
old_master = @server.current_master
|
130
|
+
@server.beetle.expects(:publish).with(:system_notification, anything)
|
131
|
+
@server.expects(:determine_new_master).returns(nil)
|
132
|
+
@server.send :switch_master
|
133
|
+
assert_equal old_master, @server.current_master
|
134
|
+
end
|
135
|
+
|
136
|
+
test "checking the availability of redis servers should publish the available servers as long as the master is available" do
|
137
|
+
@server.expects(:master_available?).returns(true)
|
138
|
+
@server.expects(:master_available!)
|
139
|
+
@server.send(:master_watcher).send(:check_availability)
|
140
|
+
end
|
141
|
+
|
142
|
+
test "checking the availability of redis servers should call master_unavailable after trying the specified number of times" do
|
143
|
+
@server.stubs(:master_available?).returns(false)
|
144
|
+
@server.expects(:master_unavailable!)
|
145
|
+
watcher = @server.send(:master_watcher)
|
146
|
+
watcher.instance_variable_set :@master_retries, 0
|
147
|
+
watcher.send(:check_availability)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
class RedisConfigurationServerInitialRedisMasterDeterminationTest < Test::Unit::TestCase
|
152
|
+
def setup
|
153
|
+
EM::Timer.stubs(:new).returns(true)
|
154
|
+
EventMachine.stubs(:add_periodic_timer).yields
|
155
|
+
@client = Client.new(Configuration.new)
|
156
|
+
@client.stubs(:listen).yields
|
157
|
+
@client.stubs(:publish)
|
158
|
+
@client.config.redis_configuration_client_ids = "rc-client-1,rc-client-2"
|
159
|
+
@server = RedisConfigurationServer.new
|
160
|
+
@server.stubs(:beetle).returns(@client)
|
161
|
+
@server.stubs(:write_redis_master_file)
|
162
|
+
@redis_master = build_master_redis_stub
|
163
|
+
@redis_slave = build_slave_redis_stub
|
164
|
+
@server.instance_variable_set(:@redis, build_redis_server_info(@redis_master, @redis_slave))
|
165
|
+
end
|
166
|
+
|
167
|
+
test "should not try to auto-detect if the master file contains a server string" do
|
168
|
+
@server.expects(:master_file_exists?).returns(true)
|
169
|
+
@server.stubs(:read_redis_master_file).returns("foobar:0000")
|
170
|
+
|
171
|
+
@server.redis.expects(:auto_detect_master).never
|
172
|
+
@server.expects(:redis_master_from_master_file).returns(@redis_master)
|
173
|
+
@server.send(:determine_initial_master)
|
174
|
+
end
|
175
|
+
|
176
|
+
test "should try to auto-detect if the master file is empty" do
|
177
|
+
@server.expects(:master_file_exists?).returns(true)
|
178
|
+
@server.stubs(:read_redis_master_file).returns("")
|
179
|
+
|
180
|
+
@server.redis.expects(:auto_detect_master).returns(@redis_master)
|
181
|
+
@server.send(:determine_initial_master)
|
182
|
+
end
|
183
|
+
|
184
|
+
test "should try to auto-detect if the master file is not present" do
|
185
|
+
@server.expects(:master_file_exists?).returns(false)
|
186
|
+
|
187
|
+
@server.redis.expects(:auto_detect_master).returns(@redis_master)
|
188
|
+
@server.send(:determine_initial_master)
|
189
|
+
end
|
190
|
+
|
191
|
+
test "should use redis master from successful auto-detection" do
|
192
|
+
@server.expects(:master_file_exists?).returns(false)
|
193
|
+
|
194
|
+
@server.expects(:write_redis_master_file).with(@redis_master.server)
|
195
|
+
@server.send(:determine_initial_master)
|
196
|
+
assert_equal @redis_master, @server.current_master
|
197
|
+
end
|
198
|
+
|
199
|
+
test "should use redis master if master in file is the only master" do
|
200
|
+
@server.expects(:master_file_exists?).returns(true)
|
201
|
+
@server.stubs(:redis_master_from_master_file).returns(@redis_master)
|
202
|
+
|
203
|
+
@server.send(:determine_initial_master)
|
204
|
+
assert_equal @redis_master, @server.current_master
|
205
|
+
end
|
206
|
+
|
207
|
+
test "should start master switch if master in file is slave" do
|
208
|
+
@server.instance_variable_set(:@redis, build_redis_server_info(@redis_slave))
|
209
|
+
@server.expects(:master_file_exists?).returns(true)
|
210
|
+
@server.stubs(:redis_master_from_master_file).returns(@redis_slave)
|
211
|
+
|
212
|
+
@server.expects(:master_unavailable!)
|
213
|
+
@server.send(:determine_initial_master)
|
214
|
+
end
|
215
|
+
|
216
|
+
test "should use master from master file if multiple masters are available" do
|
217
|
+
redis_master2 = build_master_redis_stub
|
218
|
+
@server.instance_variable_set(:@redis, build_redis_server_info(@redis_master, redis_master2))
|
219
|
+
@server.expects(:master_file_exists?).returns(true)
|
220
|
+
@server.stubs(:redis_master_from_master_file).returns(@redis_master)
|
221
|
+
|
222
|
+
@server.send(:determine_initial_master)
|
223
|
+
assert_equal @redis_master, @server.current_master
|
224
|
+
end
|
225
|
+
|
226
|
+
test "should start master switch if master in file is not available" do
|
227
|
+
not_available_redis_master = build_unknown_redis_stub
|
228
|
+
@server.instance_variable_set(:@redis, build_redis_server_info(not_available_redis_master, @redis_slave))
|
229
|
+
@server.expects(:master_file_exists?).returns(true)
|
230
|
+
@server.stubs(:redis_master_from_master_file).returns(not_available_redis_master)
|
231
|
+
|
232
|
+
@server.expects(:master_unavailable!)
|
233
|
+
@server.send(:determine_initial_master)
|
234
|
+
end
|
235
|
+
|
236
|
+
test "should raise an exception if both master file and auto-detection fails" do
|
237
|
+
not_available_redis_master = build_unknown_redis_stub
|
238
|
+
not_available_redis_slave = build_unknown_redis_stub
|
239
|
+
@server.instance_variable_set(:@redis, build_redis_server_info(not_available_redis_master, not_available_redis_slave))
|
240
|
+
@server.expects(:master_file_exists?).returns(true)
|
241
|
+
@server.expects(:read_redis_master_file).returns("")
|
242
|
+
@server.redis.expects(:auto_detect_master).returns(nil)
|
243
|
+
|
244
|
+
assert_raises Beetle::NoRedisMaster do
|
245
|
+
@server.send(:determine_initial_master)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
test "should detect a new redis_master" do
|
250
|
+
not_available_redis_master = build_unknown_redis_stub
|
251
|
+
@redis_slave.expects(:slave_of?).returns(true)
|
252
|
+
@server.instance_variable_set(:@current_master, not_available_redis_master)
|
253
|
+
@server.instance_variable_set(:@redis, build_redis_server_info(@redis_slave, not_available_redis_master))
|
254
|
+
assert_equal @redis_slave, @server.send(:determine_new_master)
|
255
|
+
end
|
256
|
+
|
257
|
+
private
|
258
|
+
|
259
|
+
def build_master_redis_stub
|
260
|
+
stub("redis master", :host => "stubbed_master", :port => 0, :server => "stubbed_master:0", :available? => true, :master? => true, :slave? => false, :role => "master")
|
261
|
+
end
|
262
|
+
|
263
|
+
def build_slave_redis_stub
|
264
|
+
stub("redis slave", :host => "stubbed_slave", :port => 0, :server => "stubbed_slave:0", :available? => true, :master? => false, :slave? => true, :role => "slave")
|
265
|
+
end
|
266
|
+
|
267
|
+
def build_unknown_redis_stub
|
268
|
+
stub("redis unknown", :host => "stubbed_unknown", :port => 0, :server => "stubbed_unknown:0", :available? => false, :master? => false, :slave? => false, :role => "unknown")
|
269
|
+
end
|
270
|
+
|
271
|
+
def build_redis_server_info(*redis_instances)
|
272
|
+
info = RedisServerInfo.new(Beetle.config, :timeout => 1)
|
273
|
+
info.instance_variable_set :@instances, redis_instances
|
274
|
+
redis_instances.each{|redis| info.send("#{redis.role}s") << redis }
|
275
|
+
info
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
|
3
|
+
module Beetle
|
4
|
+
|
5
|
+
class NonExistentRedisTest < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
@r = Redis.new(:host => "localhost", :port => 6390)
|
8
|
+
end
|
9
|
+
|
10
|
+
test "should return an empty hash for the info_with_rescue call" do
|
11
|
+
assert_equal({}, @r.info_with_rescue)
|
12
|
+
end
|
13
|
+
|
14
|
+
test "should have a role of unknown" do
|
15
|
+
assert_equal "unknown", @r.role
|
16
|
+
end
|
17
|
+
|
18
|
+
test "should not be available" do
|
19
|
+
assert !@r.available?
|
20
|
+
end
|
21
|
+
|
22
|
+
test "should not be a master" do
|
23
|
+
assert !@r.master?
|
24
|
+
end
|
25
|
+
|
26
|
+
test "should not be a slave" do
|
27
|
+
assert !@r.slave?
|
28
|
+
assert !@r.slave_of?("localhost", 6379)
|
29
|
+
end
|
30
|
+
|
31
|
+
test "should not try toconnect to the redis server on inspect" do
|
32
|
+
assert_nothing_raised { @r.inspect }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class AddedRedisMethodsTest < Test::Unit::TestCase
|
37
|
+
def setup
|
38
|
+
@r = Redis.new(:host => "localhost", :port => 6390)
|
39
|
+
end
|
40
|
+
|
41
|
+
test "should return the host, port and server string" do
|
42
|
+
assert_equal "localhost", @r.host
|
43
|
+
assert_equal 6390, @r.port
|
44
|
+
assert_equal "localhost:6390", @r.server
|
45
|
+
end
|
46
|
+
|
47
|
+
test "should stop slavery" do
|
48
|
+
@r.expects(:slaveof).with("no", "one")
|
49
|
+
@r.master!
|
50
|
+
end
|
51
|
+
|
52
|
+
test "should support slavery" do
|
53
|
+
@r.expects(:slaveof).with("localhost", 6379)
|
54
|
+
@r.slave_of!("localhost", 6379)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class RedisTimeoutTest < Test::Unit::TestCase
|
59
|
+
test "should use Redis::Timer if timeout is greater 0" do
|
60
|
+
r = Redis.new(:host => "localhost", :port => 6390, :timeout => 1)
|
61
|
+
Redis::Timer.expects(:timeout).with(1).raises(Timeout::Error)
|
62
|
+
assert_equal({}, r.info_with_rescue)
|
63
|
+
end
|
64
|
+
|
65
|
+
test "should not use Redis::Timer if timeout 0" do
|
66
|
+
r = Redis.new(:host => "localhost", :port => 6390, :timeout => 0)
|
67
|
+
Redis::Timer.expects(:timeout).never
|
68
|
+
assert_equal({}, r.info_with_rescue)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
|
3
|
+
module Beetle
|
4
|
+
|
5
|
+
class RedisMasterFileTest < Test::Unit::TestCase
|
6
|
+
include Logging
|
7
|
+
include RedisMasterFile
|
8
|
+
|
9
|
+
def setup
|
10
|
+
File.open(master_file, "w"){|f| f.puts "localhost:6379"}
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
File.unlink(master_file) if File.exist?(master_file)
|
15
|
+
end
|
16
|
+
|
17
|
+
test "should be able to check existence" do
|
18
|
+
assert master_file_exists?
|
19
|
+
File.unlink(master_file)
|
20
|
+
assert !master_file_exists?
|
21
|
+
end
|
22
|
+
|
23
|
+
test "should be able to read and write the master file"do
|
24
|
+
write_redis_master_file("localhost:6380")
|
25
|
+
assert_equal "localhost:6380", read_redis_master_file
|
26
|
+
end
|
27
|
+
|
28
|
+
test "should be able to clear the master file" do
|
29
|
+
logger.expects(:warn)
|
30
|
+
clear_redis_master_file
|
31
|
+
assert_equal "", read_redis_master_file
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def master_file
|
36
|
+
"/tmp/mumu.txt"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -2,7 +2,10 @@ require 'rubygems'
|
|
2
2
|
require 'active_support'
|
3
3
|
require 'active_support/testing/declarative'
|
4
4
|
require 'test/unit'
|
5
|
-
|
5
|
+
begin
|
6
|
+
require 'redgreen' unless ENV['TM_FILENAME']
|
7
|
+
rescue MissingSourceFile
|
8
|
+
end
|
6
9
|
require 'mocha'
|
7
10
|
require File.expand_path(File.dirname(__FILE__) + '/../lib/beetle')
|
8
11
|
|
@@ -10,7 +13,9 @@ class Test::Unit::TestCase
|
|
10
13
|
extend ActiveSupport::Testing::Declarative
|
11
14
|
end
|
12
15
|
|
16
|
+
|
13
17
|
Beetle.config.logger = Logger.new(File.dirname(__FILE__) + '/../test.log')
|
18
|
+
Beetle.config.redis_server = "localhost:6379"
|
14
19
|
|
15
20
|
def header_with_params(opts = {})
|
16
21
|
beetle_headers = Beetle::Message.publishing_options(opts)
|
@@ -18,3 +23,10 @@ def header_with_params(opts = {})
|
|
18
23
|
header.stubs(:properties).returns(beetle_headers)
|
19
24
|
header
|
20
25
|
end
|
26
|
+
|
27
|
+
def redis_stub(name, opts = {})
|
28
|
+
default_port = opts['port'] || "1234"
|
29
|
+
default_host = opts['host'] || "foo"
|
30
|
+
opts = {'host' => default_host, 'port' => default_port, 'server' => "#{default_host}:#{default_port}"}.update(opts)
|
31
|
+
stub(name, opts)
|
32
|
+
end
|