amqp-failover 0.0.1
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/.document +6 -0
- data/.gitignore +28 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +104 -0
- data/Rakefile +56 -0
- data/amqp-failover.gemspec +30 -0
- data/lib/amqp/failover/config.rb +42 -0
- data/lib/amqp/failover/configurations.rb +92 -0
- data/lib/amqp/failover/ext/amqp/client.rb +67 -0
- data/lib/amqp/failover/logger.rb +31 -0
- data/lib/amqp/failover/server_discovery.rb +45 -0
- data/lib/amqp/failover/version.rb +7 -0
- data/lib/amqp/failover.rb +111 -0
- data/lib/amqp/failover_client.rb +83 -0
- data/spec/integration/basic_spec.rb +59 -0
- data/spec/integration/failover_spec.rb +141 -0
- data/spec/logger_helper.rb +18 -0
- data/spec/server_helper.rb +77 -0
- data/spec/spec_helper.rb +49 -0
- data/spec/unit/amqp/failover/config_spec.rb +67 -0
- data/spec/unit/amqp/failover/configurations_spec.rb +79 -0
- data/spec/unit/amqp/failover/server_discovery_helper.rb +31 -0
- data/spec/unit/amqp/failover/server_discovery_spec.rb +56 -0
- data/spec/unit/amqp/failover_spec.rb +69 -0
- metadata +212 -0
@@ -0,0 +1,83 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module AMQP
|
4
|
+
module FailoverClient
|
5
|
+
include AMQP::BasicClient
|
6
|
+
|
7
|
+
attr_accessor :failover
|
8
|
+
attr_reader :fallback_monitor
|
9
|
+
|
10
|
+
attr_accessor :settings
|
11
|
+
attr_accessor :on_disconnect
|
12
|
+
|
13
|
+
def self.extended(base)
|
14
|
+
if (base.failover = base.settings.delete(:failover))
|
15
|
+
base.on_disconnect = base.method(:disconnected)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def failover_switch
|
20
|
+
if (new_settings = @failover.from(@settings))
|
21
|
+
log_message = "Could not connect to or lost connection to server #{@settings[:host]}:#{@settings[:port]}. " +
|
22
|
+
"Attempting connection to: #{new_settings[:host]}:#{new_settings[:port]}"
|
23
|
+
logger.error(log_message)
|
24
|
+
logger.info(log_message)
|
25
|
+
|
26
|
+
if @failover.options[:fallback] && @failover.primary == @settings
|
27
|
+
fallback(@failover.primary, @failover.fallback_interval)
|
28
|
+
end
|
29
|
+
@settings = new_settings
|
30
|
+
reconnect
|
31
|
+
else
|
32
|
+
raise Error, "Could not connect to server #{@settings[:host]}:#{@settings[:port]}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def logger
|
37
|
+
Failover.logger
|
38
|
+
end
|
39
|
+
|
40
|
+
def configs
|
41
|
+
@failover.configs if @failover
|
42
|
+
end
|
43
|
+
|
44
|
+
def clean_exit(msg = nil)
|
45
|
+
msg ||= "clean exit"
|
46
|
+
logger.info(msg)
|
47
|
+
logger.error(msg)
|
48
|
+
Process.exit
|
49
|
+
end
|
50
|
+
|
51
|
+
def fallback(conf = {}, retry_interval = nil)
|
52
|
+
@fallback_monitor = Failover::ServerDiscovery.monitor(conf, retry_interval) do
|
53
|
+
fallback_callback.call(conf, retry_interval)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def fallback_callback
|
58
|
+
#TODO: Figure out a way to artificially trigger EM to disconnect on fallback without channels being closed.
|
59
|
+
@fallback_callback ||= proc { |conf, retry_interval|
|
60
|
+
clean_exit("Primary server (#{conf[:host]}:#{conf[:port]}) is back. " +
|
61
|
+
"Performing clean exit to be relaunched with primary config.")
|
62
|
+
}
|
63
|
+
end
|
64
|
+
attr_writer :fallback_callback
|
65
|
+
|
66
|
+
#TODO: Figure out why I originally needed this
|
67
|
+
# def process_frame(frame)
|
68
|
+
# if mq = channels[frame.channel]
|
69
|
+
# mq.process_frame(frame)
|
70
|
+
# return
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# if frame.is_a?(AMQP::Frame::Method) && (method = frame.payload).is_a?(AMQP::Protocol::Connection::Close)
|
74
|
+
# if method.reply_text =~ /^NOT_ALLOWED/
|
75
|
+
# raise AMQP::Error, "#{method.reply_text} in #{::AMQP::Protocol.classes[method.class_id].methods[method.method_id]}"
|
76
|
+
# end
|
77
|
+
# end
|
78
|
+
# super(frame)
|
79
|
+
# end
|
80
|
+
|
81
|
+
end # FailoverClient
|
82
|
+
end # AMQP
|
83
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'spec_helper'
|
5
|
+
require 'mq'
|
6
|
+
require 'amqp'
|
7
|
+
require 'amqp/server'
|
8
|
+
require 'server_helper'
|
9
|
+
|
10
|
+
describe "Basic AMQP connection with FailoverClient loaded" do
|
11
|
+
|
12
|
+
after(:each) do
|
13
|
+
ServerHelper.clear_logs
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should be using FailoverClient" do
|
17
|
+
AMQP.client.should == AMQP::FailoverClient
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should be able to connect" do
|
21
|
+
EM.run {
|
22
|
+
port = 15672
|
23
|
+
timeout = 2
|
24
|
+
serv = start_server(port)
|
25
|
+
EM.add_timer(1.5) {
|
26
|
+
conn = AMQP.connect(:host => 'localhost', :port => 15672)
|
27
|
+
EM.add_timer(0.1) {
|
28
|
+
conn.should be_connected
|
29
|
+
serv.stop
|
30
|
+
log = serv.log
|
31
|
+
log.size.should == 3
|
32
|
+
(0..2).each { |i| log[i]['method'].should == "send" }
|
33
|
+
log[0]['class'].should == 'AMQP::Protocol::Connection::Start'
|
34
|
+
log[1]['class'].should == 'AMQP::Protocol::Connection::Tune'
|
35
|
+
log[2]['class'].should == 'AMQP::Protocol::Connection::OpenOk'
|
36
|
+
EM.stop
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should be able to connect and get disconnected" do
|
43
|
+
EM.run {
|
44
|
+
serv = start_server(25672)
|
45
|
+
EM.add_timer(0.1) {
|
46
|
+
conn = AMQP.connect(:host => 'localhost', :port => 25672)
|
47
|
+
EM.add_timer(0.1) {
|
48
|
+
conn.should be_connected
|
49
|
+
serv.stop
|
50
|
+
EM.add_timer(0.1) {
|
51
|
+
conn.should_not be_connected
|
52
|
+
EM.stop
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'spec_helper'
|
5
|
+
require 'amqp/server'
|
6
|
+
require 'server_helper'
|
7
|
+
require 'logger_helper'
|
8
|
+
|
9
|
+
describe "Failover support loaded into AMQP gem" do
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
@flog = LoggerHelper.new
|
13
|
+
AMQP::Failover.logger = @flog
|
14
|
+
end
|
15
|
+
|
16
|
+
after(:each) do
|
17
|
+
ServerHelper.clear_logs
|
18
|
+
AMQP::Failover.logger = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should be able to connect" do
|
22
|
+
port1 = 15672
|
23
|
+
EM.run {
|
24
|
+
serv = start_server(port1)
|
25
|
+
EM.add_timer(0.1) {
|
26
|
+
conn = AMQP.connect(:host => 'localhost', :port => port1)
|
27
|
+
conn.failover.should be_nil
|
28
|
+
EM.add_timer(0.1) {
|
29
|
+
conn.should be_connected
|
30
|
+
EM.stop
|
31
|
+
}
|
32
|
+
}
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should be able to connect and failover" do
|
37
|
+
port1 = 25672
|
38
|
+
port2 = 35672
|
39
|
+
EM.run {
|
40
|
+
# start mock amqp servers
|
41
|
+
serv1 = start_server(port1)
|
42
|
+
serv2 = start_server(port2)
|
43
|
+
EM.add_timer(0.1) {
|
44
|
+
# start amqp client connection and make sure it's picked the right config
|
45
|
+
conn = AMQP.connect({:hosts => [{:port => port1}, {:port => port2}]})
|
46
|
+
conn.failover.primary[:port].should == port1
|
47
|
+
conn.settings[:port].should == port1
|
48
|
+
conn.settings.should == conn.failover.primary
|
49
|
+
EM.add_timer(0.1) {
|
50
|
+
# make sure client connected to the correct server, then kill server
|
51
|
+
conn.should be_connected
|
52
|
+
serv1.log.should have(3).items
|
53
|
+
serv2.log.should have(0).items
|
54
|
+
serv1.stop
|
55
|
+
EM.add_timer(0.1) {
|
56
|
+
# make sure client performed a failover when primary server died
|
57
|
+
conn.should be_connected
|
58
|
+
[:error, :info].each do |i|
|
59
|
+
@flog.send("#{i}_log").should have(1).item
|
60
|
+
@flog.send("#{i}_log")[0][0].should match(/connect to or lost connection.+#{port1}.+attempting connection.+#{port2}/i)
|
61
|
+
end
|
62
|
+
conn.settings[:port].should == port2
|
63
|
+
serv1.log.should have(3).items
|
64
|
+
serv2.log.should have(3).items
|
65
|
+
conn.close
|
66
|
+
EM.add_timer(0.1) {
|
67
|
+
serv2.stop
|
68
|
+
EM.stop
|
69
|
+
}
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should be able to fallback when primary server returns" do
|
77
|
+
port1 = 45672
|
78
|
+
port2 = 55672
|
79
|
+
lambda {
|
80
|
+
EM.run {
|
81
|
+
# start mock amqp servers
|
82
|
+
serv1 = start_server(port1)
|
83
|
+
serv2 = start_server(port2)
|
84
|
+
EM.add_timer(0.1) {
|
85
|
+
# start amqp client connection and make sure it's picked the right config
|
86
|
+
conn = AMQP.connect({:hosts => [{:port => port1}, {:port => port2}], :fallback => true, :fallback_interval => 0.1})
|
87
|
+
conn.failover.primary[:port].should == port1
|
88
|
+
conn.settings[:port].should == port1
|
89
|
+
conn.settings.should == conn.failover.primary
|
90
|
+
EM.add_timer(0.1) {
|
91
|
+
# make sure client connected to the correct server, then kill server
|
92
|
+
conn.should be_connected
|
93
|
+
serv1.log.should have(3).items
|
94
|
+
serv2.log.should have(0).items
|
95
|
+
serv1.stop
|
96
|
+
EM.add_timer(0.1) {
|
97
|
+
# make sure client performed a failover when primary server died
|
98
|
+
conn.should be_connected
|
99
|
+
[:error, :info].each do |i|
|
100
|
+
@flog.send("#{i}_log").should have(1).item
|
101
|
+
@flog.send("#{i}_log")[0][0].should match(/connect to or lost connection.+#{port1}.+attempting connection.+#{port2}/i)
|
102
|
+
end
|
103
|
+
conn.settings[:port].should == port2
|
104
|
+
serv1.log.should have(3).items
|
105
|
+
serv2.log.should have(3).items
|
106
|
+
serv3 = start_server(port1)
|
107
|
+
EM.add_timer(0.2) {
|
108
|
+
# by this point client should have raised a SystemExit exception
|
109
|
+
serv2.stop
|
110
|
+
EM.stop
|
111
|
+
}
|
112
|
+
}
|
113
|
+
}
|
114
|
+
}
|
115
|
+
}
|
116
|
+
}.should raise_error(SystemExit, "exit")
|
117
|
+
[:error, :info].each do |i|
|
118
|
+
@flog.send("#{i}_log").should have(2).item
|
119
|
+
@flog.send("#{i}_log")[1][0].should match(/primary server.+45672.+performing clean exit/i)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should abide to :primary_config option" do
|
124
|
+
port1 = 75672
|
125
|
+
port2 = 65672
|
126
|
+
EM.run {
|
127
|
+
serv = start_server(port1)
|
128
|
+
EM.add_timer(0.1) {
|
129
|
+
conn = AMQP.connect({:hosts => [{:port => port1}, {:port => port2}], :primary_config => 1})
|
130
|
+
conn.failover.primary[:port].should == port2
|
131
|
+
conn.settings[:port].should == port2
|
132
|
+
conn.settings.should == conn.failover.primary
|
133
|
+
EM.add_timer(0.1) {
|
134
|
+
conn.should be_connected
|
135
|
+
EM.stop
|
136
|
+
}
|
137
|
+
}
|
138
|
+
}
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class LoggerHelper
|
4
|
+
|
5
|
+
attr_accessor :error_log
|
6
|
+
attr_accessor :info_log
|
7
|
+
|
8
|
+
def info(*args)
|
9
|
+
@info_log ||= []
|
10
|
+
@info_log << args
|
11
|
+
end
|
12
|
+
|
13
|
+
def error(*args)
|
14
|
+
@error_log ||= []
|
15
|
+
@error_log << args
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'mq'
|
5
|
+
require 'amqp'
|
6
|
+
require 'amqp/server'
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
class ServerHelper
|
10
|
+
|
11
|
+
attr_accessor :stdin
|
12
|
+
attr_accessor :stdout
|
13
|
+
attr_accessor :stderr
|
14
|
+
attr_accessor :pid
|
15
|
+
|
16
|
+
|
17
|
+
def initialize(port = nil, timeout = nil)
|
18
|
+
@port = port
|
19
|
+
@timout = timeout
|
20
|
+
File.open(log_file, 'w') {}
|
21
|
+
@pid = start(port, timeout)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.clear_logs
|
25
|
+
Dir.glob(File.expand_path('server_helper*.log', File.dirname(__FILE__))).each do |file|
|
26
|
+
File.delete(file)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def start(port = nil, timeout = nil)
|
31
|
+
port ||= 15672
|
32
|
+
timeout ||= 2
|
33
|
+
EM.fork_reactor {
|
34
|
+
$PORT = port
|
35
|
+
EM.start_server('localhost', port, AmqpServer)
|
36
|
+
EM.add_timer(timeout) { EM.stop }
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def stop
|
41
|
+
Process.kill('TERM', @pid)
|
42
|
+
end
|
43
|
+
|
44
|
+
def kill
|
45
|
+
Process.kill('KILL', @pid)
|
46
|
+
end
|
47
|
+
|
48
|
+
def log
|
49
|
+
File.open(log_file).to_a.map{ |l| JSON.parse(l) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def log_file
|
53
|
+
File.expand_path("server_helper-port#{@port}.log", File.dirname(__FILE__))
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
module AmqpServer
|
59
|
+
include AMQP::Server
|
60
|
+
|
61
|
+
# customize log output
|
62
|
+
def log(*args)
|
63
|
+
args = {:method => args[0], :class => args[1].payload.class, :pid => Process.pid}
|
64
|
+
filename = File.expand_path("server_helper-port#{$PORT}.log", File.dirname(__FILE__))
|
65
|
+
File.open(filename, 'a') do |f|
|
66
|
+
f.write("#{args.to_json}\n")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
# Helper methods
|
73
|
+
#
|
74
|
+
|
75
|
+
def start_server(port = nil, timeout = nil)
|
76
|
+
ServerHelper.new(port, timeout)
|
77
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# add project-relative load paths
|
4
|
+
$LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
5
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
|
6
|
+
|
7
|
+
# require stuff
|
8
|
+
require 'rubygems'
|
9
|
+
|
10
|
+
begin
|
11
|
+
require 'mq'
|
12
|
+
rescue LoadError => e
|
13
|
+
require 'amqp'
|
14
|
+
end
|
15
|
+
require 'amqp/failover'
|
16
|
+
|
17
|
+
require 'rspec'
|
18
|
+
require 'rspec/autorun'
|
19
|
+
|
20
|
+
|
21
|
+
#
|
22
|
+
# Helper methods
|
23
|
+
#
|
24
|
+
|
25
|
+
def wait_while(timeout = 10, retry_interval = 0.1, &block)
|
26
|
+
start = Time.now
|
27
|
+
while block.call
|
28
|
+
break if (Time.now - start).to_i >= timeout
|
29
|
+
sleep(retry_interval)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# stolen from Pid::running? from daemons gem
|
34
|
+
def pid_running?(pid)
|
35
|
+
return false unless pid
|
36
|
+
|
37
|
+
# Check if process is in existence
|
38
|
+
# The simplest way to do this is to send signal '0'
|
39
|
+
# (which is a single system call) that doesn't actually
|
40
|
+
# send a signal
|
41
|
+
begin
|
42
|
+
Process.kill(0, pid)
|
43
|
+
return true
|
44
|
+
rescue Errno::ESRCH
|
45
|
+
return false
|
46
|
+
rescue ::Exception # for example on EPERM (process exists but does not belong to us)
|
47
|
+
return true
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'spec_helper'
|
5
|
+
|
6
|
+
describe 'AMQP::Failover::Config' do
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
configs = [
|
10
|
+
{:host => 'rabbit0.local'},
|
11
|
+
{:host => 'rabbit1.local'},
|
12
|
+
{:host => 'rabbit2.local', :port => 5673}
|
13
|
+
]
|
14
|
+
@configs = configs.map { |conf| AMQP.settings.merge(conf) }
|
15
|
+
@fail = AMQP::Failover.new(@configs)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should initialize" do
|
19
|
+
fail = AMQP::Failover::Config.new(@configs[0])
|
20
|
+
fail.should == @configs[0]
|
21
|
+
fail.last_fail.should be_nil
|
22
|
+
|
23
|
+
now = Time.now
|
24
|
+
fail = AMQP::Failover::Config.new(@configs[1], now)
|
25
|
+
fail.should == @configs[1]
|
26
|
+
fail.last_fail.should == now
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should order properly with #<=>" do
|
30
|
+
one_hour_ago = (Time.now - 3600)
|
31
|
+
two_hours_ago = (Time.now - 7200)
|
32
|
+
|
33
|
+
fail = [ AMQP::Failover::Config.new(@configs[0]),
|
34
|
+
AMQP::Failover::Config.new(@configs[1], one_hour_ago),
|
35
|
+
AMQP::Failover::Config.new(@configs[2], two_hours_ago) ]
|
36
|
+
|
37
|
+
(fail[1] <=> fail[0]).should == -1
|
38
|
+
(fail[0] <=> fail[0]).should == 0
|
39
|
+
(fail[0] <=> fail[1]).should == 1
|
40
|
+
|
41
|
+
(fail[1] <=> fail[2]).should == -1
|
42
|
+
(fail[1] <=> fail[1]).should == 0
|
43
|
+
(fail[2] <=> fail[1]).should == 1
|
44
|
+
|
45
|
+
fail.sort[0].last_fail.should == one_hour_ago
|
46
|
+
fail.sort[1].last_fail.should == two_hours_ago
|
47
|
+
fail.sort[2].last_fail.should == nil
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should be ordered by last_fail" do
|
51
|
+
result = [ AMQP::Failover::Config.new(@configs[1], (Time.now - 60)),
|
52
|
+
AMQP::Failover::Config.new(@configs[2], (Time.now - (60*25))),
|
53
|
+
AMQP::Failover::Config.new(@configs[0], (Time.now - 3600)) ]
|
54
|
+
|
55
|
+
origin = [ AMQP::Failover::Config.new(@configs[0], (Time.now - 3600)),
|
56
|
+
AMQP::Failover::Config.new(@configs[1], (Time.now - 60)),
|
57
|
+
AMQP::Failover::Config.new(@configs[2], (Time.now - (60*25))) ]
|
58
|
+
origin.sort.should == result
|
59
|
+
|
60
|
+
origin = [ AMQP::Failover::Config.new(@configs[0]),
|
61
|
+
AMQP::Failover::Config.new(@configs[1], (Time.now - 60)),
|
62
|
+
AMQP::Failover::Config.new(@configs[2], (Time.now - (60*25))) ]
|
63
|
+
origin.sort.should == result
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'spec_helper'
|
5
|
+
|
6
|
+
describe 'AMQP::Failover::Configurations' do
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
@conf = AMQP::Failover::Configurations.new
|
10
|
+
@raw_configs = [
|
11
|
+
{:host => 'rabbit0.local'},
|
12
|
+
{:host => 'rabbit1.local'},
|
13
|
+
{:host => 'rabbit2.local', :port => 5673}
|
14
|
+
]
|
15
|
+
@configs = @raw_configs.map { |conf| AMQP.settings.merge(conf) }
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should initialize" do
|
19
|
+
confs = AMQP::Failover::Configurations.new(@raw_configs)
|
20
|
+
confs.each_with_index do |conf, i|
|
21
|
+
conf.should be_a(AMQP::Failover::Config)
|
22
|
+
conf.should == @configs[i]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should set and get configs" do
|
27
|
+
@conf.primary_ref.should == 0
|
28
|
+
@conf.should have(0).items
|
29
|
+
|
30
|
+
@conf.set(@raw_configs[0])
|
31
|
+
@conf.should have(1).items
|
32
|
+
@conf.get(0).should == @configs[0]
|
33
|
+
@conf[0].should == @configs[0]
|
34
|
+
|
35
|
+
@conf.set(@raw_configs[1])
|
36
|
+
@conf.should have(2).items
|
37
|
+
@conf.get(1).should == @configs[1]
|
38
|
+
@conf[1].should == @configs[1]
|
39
|
+
|
40
|
+
# should just create a ref, as config exists
|
41
|
+
@conf.set(@raw_configs[1], :the_one)
|
42
|
+
@conf.should have(2).items
|
43
|
+
@conf.get(1).should == @configs[1]
|
44
|
+
@conf[:the_one].should == @configs[1]
|
45
|
+
|
46
|
+
@conf.load_array(@raw_configs)
|
47
|
+
@conf.should have(3).items
|
48
|
+
@conf.primary.should == @configs[0]
|
49
|
+
@conf.primary_ref = 1
|
50
|
+
@conf.primary.should == @configs[1]
|
51
|
+
@conf[:primary].should == @configs[1]
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should #find_next" do
|
55
|
+
@conf.load(@raw_configs)
|
56
|
+
@conf.should have(3).items
|
57
|
+
@conf.find_next(@configs[0]).should == @configs[1]
|
58
|
+
@conf.find_next(@configs[1]).should == @configs[2]
|
59
|
+
@conf.find_next(@configs[2]).should == @configs[0]
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should #load_hash" do
|
63
|
+
@conf.should have(0).items
|
64
|
+
@conf.load_hash(@raw_configs[0])
|
65
|
+
@conf.should have(1).items
|
66
|
+
@conf.primary.should == @configs[0]
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should #load_array" do
|
70
|
+
@conf.load_hash(:host => 'rabbid-rabbit')
|
71
|
+
@conf.should have(1).items
|
72
|
+
@conf.load_array(@raw_configs)
|
73
|
+
@conf.should have(3).items
|
74
|
+
@conf.should == @configs
|
75
|
+
@conf.primary.should == @configs[0]
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class ServerDiscoveryHelper < AMQP::Failover::ServerDiscovery
|
2
|
+
|
3
|
+
class << self
|
4
|
+
alias :real_start_monitoring :start_monitoring
|
5
|
+
def start_monitoring(*args, &block)
|
6
|
+
$called << :start_monitoring
|
7
|
+
real_start_monitoring(*args, &block)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
alias :real_initialize :initialize
|
12
|
+
def initialize(*args)
|
13
|
+
$called << :initialize
|
14
|
+
EM.start_server('127.0.0.1', 9999) if $start_count == 2
|
15
|
+
$start_count += 1
|
16
|
+
real_initialize(*args)
|
17
|
+
end
|
18
|
+
|
19
|
+
alias :real_connection_completed :connection_completed
|
20
|
+
def connection_completed
|
21
|
+
$called << :connection_completed
|
22
|
+
real_connection_completed
|
23
|
+
end
|
24
|
+
|
25
|
+
alias :real_close_connection :close_connection
|
26
|
+
def close_connection
|
27
|
+
$called << :close_connection
|
28
|
+
real_close_connection
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'spec_helper'
|
5
|
+
require 'server_discovery_helper'
|
6
|
+
|
7
|
+
describe 'AMQP::Failover::ServerDiscovery' do
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
$called = []
|
11
|
+
$start_count = 0
|
12
|
+
@args = { :host => 'localhost', :port => 9999 }
|
13
|
+
@retry_interval = 0.01
|
14
|
+
end
|
15
|
+
|
16
|
+
after(:all) do
|
17
|
+
$called = nil
|
18
|
+
$start_count = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should initialize" do
|
22
|
+
EM.run {
|
23
|
+
EM.start_server('127.0.0.1', 9999)
|
24
|
+
@mon = ServerDiscoveryHelper.monitor(@args, @retry_interval) do
|
25
|
+
$called << :done_block
|
26
|
+
EM.stop_event_loop
|
27
|
+
end
|
28
|
+
}
|
29
|
+
$start_count.should == 1
|
30
|
+
$called.should have(5).items
|
31
|
+
$called.uniq.should have(5).items
|
32
|
+
$called.should include(:start_monitoring)
|
33
|
+
$called.should include(:initialize)
|
34
|
+
$called.should include(:connection_completed)
|
35
|
+
$called.should include(:close_connection)
|
36
|
+
$called.should include(:done_block)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should retry on error" do
|
40
|
+
EM.run {
|
41
|
+
@mon = ServerDiscoveryHelper.monitor(@args, @retry_interval) do
|
42
|
+
$called << :done_block
|
43
|
+
EM.stop_event_loop
|
44
|
+
end
|
45
|
+
}
|
46
|
+
$start_count.should >= 3
|
47
|
+
$called.should have($start_count + 4).items
|
48
|
+
$called.uniq.should have(5).items
|
49
|
+
$called.should include(:start_monitoring)
|
50
|
+
$called.should include(:initialize)
|
51
|
+
$called.should include(:connection_completed)
|
52
|
+
$called.should include(:close_connection)
|
53
|
+
$called.should include(:done_block)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|