beetle 2.2.3 → 2.3.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.
- checksums.yaml +4 -4
- data/README.rdoc +1 -1
- data/RELEASE_NOTES.rdoc +21 -1
- data/beetle.gemspec +5 -2
- data/examples/attempts.rb +1 -0
- data/features/redis_auto_failover.feature +46 -0
- data/features/step_definitions/redis_auto_failover_steps.rb +27 -12
- data/features/support/beetle_handler +9 -2
- data/features/support/env.rb +6 -2
- data/features/support/test_daemons/redis_configuration_server.rb +4 -4
- data/lib/beetle.rb +0 -1
- data/lib/beetle/configuration.rb +13 -0
- data/lib/beetle/dead_lettering.rb +36 -26
- data/lib/beetle/deduplication_store.rb +19 -2
- data/lib/beetle/message.rb +10 -2
- data/lib/beetle/publisher.rb +12 -10
- data/lib/beetle/version.rb +1 -1
- data/script/console +4 -4
- data/test/beetle/dead_lettering_test.rb +36 -15
- data/test/beetle/deduplication_store_test.rb +13 -0
- data/test/beetle/publisher_test.rb +1 -0
- metadata +20 -23
- data/features/#redis_auto_failover.feature# +0 -165
- data/lib/beetle/redis_master_file.rb +0 -30
- data/test/beetle/redis_master_file_test.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c8a5f1b489f657fd5e286f6461b13ee314b6b4abe3dae6452bb53ca81ce23255
|
4
|
+
data.tar.gz: 183a0d96adf42fe4648f946203527edd1b065b7c961b62b825e6f5c6483edf7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3eaa2e3b642b8c3dd5d5062a4327d5499e87c3c02d27493b33f9f42fa1bb2964e2f6b5af386c88525b2343e075ba61e42fe9a75ce459d2a98ef949e418ffe7e1
|
7
|
+
data.tar.gz: cf80066e492795627ef460eec5c05a222a5af569d87e62084ec865939e715a6f4c6ca902f618d9039f7d650c9208b694a30e28dd81bd23571d71b1a1ff7ea287
|
data/README.rdoc
CHANGED
@@ -111,7 +111,7 @@ For tests, you'll need
|
|
111
111
|
|
112
112
|
You can find out more about our work on our {dev blog}[http://devblog.xing.com].
|
113
113
|
|
114
|
-
Copyright (c) 2010-
|
114
|
+
Copyright (c) 2010-2019 {XING AG}[http://www.xing.com/]
|
115
115
|
|
116
116
|
Released under the MIT license. For full details see MIT-LICENSE included in this
|
117
117
|
distribution.
|
data/RELEASE_NOTES.rdoc
CHANGED
@@ -1,7 +1,27 @@
|
|
1
1
|
= Release Notes
|
2
2
|
|
3
|
+
== Version 2.3.0
|
4
|
+
* redis failover: support multiple redis failover instances. This has
|
5
|
+
required a change in the redis master file format. The master file
|
6
|
+
content is now either the old format (host:port) for systems using a
|
7
|
+
single redis failover system or a mapping from system names to
|
8
|
+
host:port combinations. For example,
|
9
|
+
"system1/master1:6379\nsystem2/master" specifies to systems with
|
10
|
+
their corresponding redis masters. Beetle client uses the configured
|
11
|
+
system name to tind the master it should use.
|
12
|
+
* support lazy queues: setting :lazy_quques_enabled on the beetle
|
13
|
+
cofiguration will enable queue_mode: "lazy" for all queues of
|
14
|
+
declared on the beetle client.
|
15
|
+
* improved calculation of channel close an connection disconnect
|
16
|
+
timeouts for beetle publisher to avoid warnings in RabbitMQ logs.
|
17
|
+
* use SecureRandom.uuid instead of UUID4R::uuid(4) if UUID4R cannot
|
18
|
+
be loaded.
|
19
|
+
|
20
|
+
== Version 2.2.4
|
21
|
+
* redis failover: prevent starting a new master switch while one is running
|
22
|
+
|
3
23
|
== Version 2.2.3
|
4
|
-
* redis failover server logs errors when redis oparations fail
|
24
|
+
* redis failover: server logs errors when redis oparations fail
|
5
25
|
|
6
26
|
== Version 2.2.2
|
7
27
|
|
data/beetle.gemspec
CHANGED
@@ -18,16 +18,19 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.rdoc_options = ["--charset=UTF-8"]
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
s.test_files = Dir['test/**/*.rb']
|
21
|
+
s.metadata = {
|
22
|
+
"changelog_uri" => "https://github.com/xing/beetle/blob/master/RELEASE_NOTES.rdoc"
|
23
|
+
}
|
21
24
|
|
22
25
|
s.specification_version = 3
|
23
|
-
s.add_runtime_dependency "
|
24
|
-
s.add_runtime_dependency "bunny", "~> 0.7.10"
|
26
|
+
s.add_runtime_dependency "bunny", "~> 0.7.12"
|
25
27
|
s.add_runtime_dependency "redis", ">= 2.2.2"
|
26
28
|
s.add_runtime_dependency "hiredis", ">= 0.4.5"
|
27
29
|
s.add_runtime_dependency "amq-protocol", "= 2.0.1"
|
28
30
|
s.add_runtime_dependency "amqp", "= 1.6.0"
|
29
31
|
s.add_runtime_dependency "activesupport", ">= 2.3.4"
|
30
32
|
|
33
|
+
s.add_development_dependency "uuid4r", ">= 0.1.2"
|
31
34
|
s.add_development_dependency "activerecord", "~> 5.0"
|
32
35
|
s.add_development_dependency "cucumber", "~> 2.4.0"
|
33
36
|
s.add_development_dependency "daemon_controller", "~> 1.2.0"
|
data/examples/attempts.rb
CHANGED
@@ -16,6 +16,7 @@ Beetle.config.logger.level = Logger::INFO
|
|
16
16
|
# setup client
|
17
17
|
$client = Beetle::Client.new
|
18
18
|
$client.config.dead_lettering_enabled = true
|
19
|
+
$client.config.lazy_queues_enabled = true
|
19
20
|
$client.configure(:key => "my.test.message") do
|
20
21
|
message(:test)
|
21
22
|
queue(:test)
|
@@ -23,6 +23,52 @@ Feature: Redis auto failover
|
|
23
23
|
Given a redis server "redis-1" exists as master
|
24
24
|
Then the role of redis server "redis-1" should be "slave"
|
25
25
|
|
26
|
+
Scenario: Successful single redis master switch with multiple failover sets
|
27
|
+
Given a redis server "redis-3" exists as master
|
28
|
+
And a redis server "redis-4" exists as slave of "redis-3"
|
29
|
+
Given a redis configuration server using redis servers "a/redis-1,redis-2;b/redis-3,redis-4" with clients "rc-client-1,rc-client-2" exists
|
30
|
+
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
31
|
+
And a redis configuration client "rc-client-2" using redis servers "redis-1,redis-2" exists
|
32
|
+
And a beetle handler using the redis-master file from "rc-client-1" exists
|
33
|
+
And redis server "redis-1" is down
|
34
|
+
And the retry timeout for the redis master check is reached
|
35
|
+
Then a system notification for "redis-1" not being available should be sent
|
36
|
+
And the role of redis server "redis-2" should be "master"
|
37
|
+
And the redis master file of the redis configuration server should contain "redis-2"
|
38
|
+
And the redis master file of the redis configuration server should contain "redis-3"
|
39
|
+
And the redis master of "rc-client-1" in system "a" should be "redis-2"
|
40
|
+
And the redis master of "rc-client-2" in system "a" should be "redis-2"
|
41
|
+
And the redis master of "rc-client-1" in system "b" should be "redis-3"
|
42
|
+
And the redis master of "rc-client-2" in system "b" should be "redis-3"
|
43
|
+
And the redis master of the beetle handler should be "redis-2"
|
44
|
+
And a system notification for switching from "redis-1" to "redis-2" should be sent
|
45
|
+
Given a redis server "redis-1" exists as master
|
46
|
+
Then the role of redis server "redis-1" should be "slave"
|
47
|
+
|
48
|
+
Scenario: Successful double redis master switch with multiple failover sets
|
49
|
+
Given a redis server "redis-3" exists as master
|
50
|
+
And a redis server "redis-4" exists as slave of "redis-3"
|
51
|
+
Given a redis configuration server using redis servers "a/redis-1,redis-2;b/redis-3,redis-4" with clients "rc-client-1,rc-client-2" exists
|
52
|
+
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
53
|
+
And a redis configuration client "rc-client-2" using redis servers "redis-1,redis-2" exists
|
54
|
+
And a beetle handler using the redis-master file from "rc-client-1" exists
|
55
|
+
And redis server "redis-1" is down
|
56
|
+
And redis server "redis-3" is down
|
57
|
+
And the retry timeout for the redis master check is reached
|
58
|
+
And the role of redis server "redis-2" should be "master"
|
59
|
+
And the role of redis server "redis-4" should be "master"
|
60
|
+
And the redis master file of the redis configuration server should contain "redis-2"
|
61
|
+
And the redis master file of the redis configuration server should contain "redis-4"
|
62
|
+
And the redis master of "rc-client-1" in system "a" should be "redis-2"
|
63
|
+
And the redis master of "rc-client-2" in system "a" should be "redis-2"
|
64
|
+
And the redis master of "rc-client-1" in system "b" should be "redis-4"
|
65
|
+
And the redis master of "rc-client-2" in system "b" should be "redis-4"
|
66
|
+
And the redis master of the beetle handler should be "redis-2"
|
67
|
+
Given a redis server "redis-1" exists as master
|
68
|
+
Then the role of redis server "redis-1" should be "slave"
|
69
|
+
Given a redis server "redis-3" exists as master
|
70
|
+
Then the role of redis server "redis-3" should be "slave"
|
71
|
+
|
26
72
|
Scenario: Redis master only temporarily down (no switch necessary)
|
27
73
|
Given a redis configuration server using redis servers "redis-1,redis-2" with clients "rc-client-1,rc-client-2" exists
|
28
74
|
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
@@ -22,13 +22,22 @@ Given /^redis server "([^\"]*)" is slave of "([^\"]*)"$/ do |redis_name, redis_m
|
|
22
22
|
end
|
23
23
|
|
24
24
|
Given /^a redis configuration server using redis servers "([^\"]*)" with clients "([^\"]*)" (?:and confidence level "([^\"]*)" )?exists$/ do |redis_names, redis_configuration_client_names, confidence_level|
|
25
|
-
redis_servers = redis_names.split("
|
25
|
+
redis_servers = redis_names.split(";").map do |system_spec|
|
26
|
+
if system_spec.include?("/")
|
27
|
+
system_name, servers = system_spec.split("/", 2)
|
28
|
+
else
|
29
|
+
system_name, servers = nil, system_spec
|
30
|
+
end
|
31
|
+
servers = servers.split(",").map { |redis_name| TestDaemons::Redis[redis_name].ip_with_port }.join(",")
|
32
|
+
system_name.nil? ? servers : "#{system_name}/#{servers}"
|
33
|
+
end.join(";")
|
26
34
|
TestDaemons::RedisConfigurationServer.start(redis_servers, redis_configuration_client_names, (confidence_level || 100).to_i)
|
27
35
|
end
|
28
36
|
|
29
37
|
Given /^a redis configuration client "([^\"]*)" using redis servers "([^\"]*)" exists$/ do |redis_configuration_client_name, redis_names|
|
30
|
-
|
31
|
-
|
38
|
+
redis_names.split(";").each do |system_spec|
|
39
|
+
servers = system_spec.sub(/^.*\//, '')
|
40
|
+
servers.split(",").map { |redis_name| TestDaemons::Redis[redis_name].ip_with_port }
|
32
41
|
end
|
33
42
|
TestDaemons::RedisConfigurationClient[redis_configuration_client_name].start
|
34
43
|
end
|
@@ -73,7 +82,7 @@ end
|
|
73
82
|
Given /^an old redis master file for "([^\"]*)" with master "([^\"]*)" exists$/ do |redis_configuration_client_name, redis_name|
|
74
83
|
master_file = redis_master_file(redis_configuration_client_name)
|
75
84
|
File.open(master_file, 'w') do |f|
|
76
|
-
f.puts TestDaemons::Redis[redis_name].ip_with_port
|
85
|
+
f.puts "system/#{TestDaemons::Redis[redis_name].ip_with_port}"
|
77
86
|
end
|
78
87
|
end
|
79
88
|
|
@@ -87,22 +96,28 @@ Then /^the role of redis server "([^\"]*)" should be "(master|slave)"$/ do |redi
|
|
87
96
|
assert expected_role, "#{redis_name} is not a #{role}"
|
88
97
|
end
|
89
98
|
|
90
|
-
Then /^the redis master of "([^\"]*)" should be "([^\"]*)"$/ do |redis_configuration_client_name, redis_name|
|
99
|
+
Then /^the redis master of "([^\"]*)" (?:in system "([^"]*)" )?should be "([^\"]*)"$/ do |redis_configuration_client_name, system_name, redis_name|
|
100
|
+
system_name ||= "system"
|
91
101
|
master_file = redis_master_file(redis_configuration_client_name)
|
92
102
|
master = false
|
93
|
-
server_info =
|
103
|
+
server_info = ''
|
94
104
|
10.times do
|
105
|
+
server_name = TestDaemons::Redis[redis_name].ip_with_port
|
95
106
|
server_info = File.read(master_file).chomp if File.exist?(master_file)
|
96
|
-
|
107
|
+
if server_info.include?("/")
|
108
|
+
master = true and break if server_info =~ /#{system_name}\/#{server_name}/m
|
109
|
+
else
|
110
|
+
master = true and break if server_info == server_name
|
111
|
+
end
|
97
112
|
sleep 1
|
98
113
|
end
|
99
114
|
assert master, "#{redis_name} is not master of #{redis_configuration_client_name}, master file content: #{server_info.inspect}"
|
100
115
|
end
|
101
116
|
|
102
|
-
Then /^the redis master file of the redis configuration server should contain "([^"]*)"$/ do |redis_name|
|
117
|
+
Then /^the redis master file of the redis configuration server should contain "([^"]*)"$/ do |redis_name|
|
103
118
|
master_file = TestDaemons::RedisConfigurationServer.redis_master_file
|
104
119
|
file_contents = File.read(master_file).chomp
|
105
|
-
|
120
|
+
assert_match /#{TestDaemons::Redis[redis_name].ip_with_port}/, file_contents
|
106
121
|
end
|
107
122
|
|
108
123
|
Then /^the redis master of "([^\"]*)" should be undefined$/ do |redis_configuration_client_name|
|
@@ -111,7 +126,7 @@ Then /^the redis master of "([^\"]*)" should be undefined$/ do |redis_configurat
|
|
111
126
|
server_info = nil
|
112
127
|
10.times do
|
113
128
|
server_info = File.read(master_file).chomp if File.exist?(master_file)
|
114
|
-
empty = server_info
|
129
|
+
empty = server_info !~ /:\d+/
|
115
130
|
break if empty
|
116
131
|
sleep 1
|
117
132
|
end
|
@@ -125,7 +140,7 @@ Then /^the redis master of the beetle handler should be "([^\"]*)"$/ do |redis_n
|
|
125
140
|
config.queue(:echo)
|
126
141
|
config.message(:echo)
|
127
142
|
end
|
128
|
-
|
143
|
+
assert_match /#{TestDaemons::Redis[redis_name].ip_with_port}/, client.rpc(:echo, 'nil').second
|
129
144
|
end
|
130
145
|
|
131
146
|
Then /^a system notification for "([^\"]*)" not being available should be sent$/ do |redis_name|
|
@@ -157,7 +172,7 @@ end
|
|
157
172
|
|
158
173
|
Given /^an immediate master switch is initiated and responds with (\d+)$/ do |response_code|
|
159
174
|
response = TestDaemons::RedisConfigurationServer.initiate_master_switch
|
160
|
-
assert_equal response_code, response.code
|
175
|
+
assert_equal response_code, response.code, "unexpected response code #{response.code}, message: #{response.body}"
|
161
176
|
sleep 1
|
162
177
|
end
|
163
178
|
|
@@ -24,9 +24,16 @@ Daemons.run_proc("beetle_handler", :log_output => true, :dir_mode => :normal, :d
|
|
24
24
|
client = Beetle::Client.new.configure :auto_delete => true do |config|
|
25
25
|
config.queue(:echo)
|
26
26
|
config.message(:echo)
|
27
|
-
config.handler(:echo)
|
27
|
+
config.handler(:echo) do |message|
|
28
|
+
begin
|
29
|
+
client.deduplication_store.redis.server
|
30
|
+
rescue
|
31
|
+
master_file_content = File.read(Beetle.config.redis_server)
|
32
|
+
"no redis master: exception: #{$!.class}(#{$!}), master_file: '#{master_file_content}'"
|
33
|
+
end
|
34
|
+
end
|
28
35
|
end
|
29
36
|
client.listen do
|
30
|
-
puts "Started beetle handler"
|
37
|
+
puts "Started beetle handler for system: #{Beetle.config.system_name}"
|
31
38
|
end
|
32
39
|
end
|
data/features/support/env.rb
CHANGED
@@ -7,6 +7,7 @@ World do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
Before do
|
10
|
+
cleanup_master_files
|
10
11
|
`ruby features/support/system_notification_logger start`
|
11
12
|
end
|
12
13
|
|
@@ -23,8 +24,6 @@ def cleanup_test_env
|
|
23
24
|
TestDaemons::RedisConfigurationServer.stop
|
24
25
|
|
25
26
|
`ruby features/support/beetle_handler stop`
|
26
|
-
redis_master_files = tmp_path + "/redis-master-*"
|
27
|
-
`rm -f #{redis_master_files}`
|
28
27
|
|
29
28
|
`ruby features/support/system_notification_logger stop`
|
30
29
|
# `rm -f #{system_notification_log_path}`
|
@@ -32,6 +31,11 @@ def cleanup_test_env
|
|
32
31
|
TestDaemons::Redis.stop_all
|
33
32
|
end
|
34
33
|
|
34
|
+
def cleanup_master_files
|
35
|
+
redis_master_files = tmp_path + "/redis-master-*"
|
36
|
+
`rm -f #{redis_master_files}`
|
37
|
+
end
|
38
|
+
|
35
39
|
def redis_master_file(client_name)
|
36
40
|
tmp_path + "/redis-master-#{client_name}"
|
37
41
|
end
|
@@ -12,10 +12,10 @@ module TestDaemons
|
|
12
12
|
@@confidence_level = 100
|
13
13
|
|
14
14
|
def self.start(redis_servers, redis_configuration_clients, confidence_level)
|
15
|
-
stop
|
16
15
|
@@redis_servers = redis_servers
|
17
16
|
@@redis_configuration_clients = redis_configuration_clients
|
18
17
|
@@confidence_level = confidence_level
|
18
|
+
stop
|
19
19
|
daemon_controller.start
|
20
20
|
end
|
21
21
|
|
@@ -27,7 +27,7 @@ module TestDaemons
|
|
27
27
|
clients_parameter_string = @@redis_configuration_clients.blank? ? "" : "--client-ids #{@@redis_configuration_clients}"
|
28
28
|
DaemonController.new(
|
29
29
|
:identifier => "Redis configuration test server",
|
30
|
-
:start_command => "./beetle configuration_server -v -d --redis-master-file #{redis_master_file} --redis-servers #{@@redis_servers} #{clients_parameter_string} --redis-master-retry-interval 1 --pid-file #{pid_file} --log-file #{log_file} --redis-failover-confidence-level #{@@confidence_level}",
|
30
|
+
:start_command => "./beetle configuration_server -v -d --redis-master-file #{redis_master_file} --redis-servers '#{@@redis_servers}' #{clients_parameter_string} --redis-master-retry-interval 1 --pid-file #{pid_file} --log-file #{log_file} --redis-failover-confidence-level #{@@confidence_level}",
|
31
31
|
:ping_command => lambda{ answers_text_requests? },
|
32
32
|
:pid_file => pid_file,
|
33
33
|
:log_file => log_file,
|
@@ -86,9 +86,9 @@ module TestDaemons
|
|
86
86
|
response
|
87
87
|
end
|
88
88
|
|
89
|
-
def self.initiate_master_switch
|
89
|
+
def self.initiate_master_switch(system_name = "system")
|
90
90
|
http = Net::HTTP.new('127.0.0.1', 9650)
|
91
|
-
response = http.post
|
91
|
+
response = http.post "/initiate_master_switch?system_name=#{system_name}", ""
|
92
92
|
response
|
93
93
|
end
|
94
94
|
|
data/lib/beetle.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
$:.unshift(File.expand_path('..', __FILE__))
|
2
2
|
require 'bunny' # which bunny picks up
|
3
3
|
require 'qrack/errors' # needed by the publisher
|
4
|
-
require 'uuid4r'
|
5
4
|
require 'redis/connection/hiredis' # require *before* redis as specified in the redis-rb gem docs
|
6
5
|
require 'redis'
|
7
6
|
require 'active_support/all'
|
data/lib/beetle/configuration.rb
CHANGED
@@ -62,6 +62,11 @@ module Beetle
|
|
62
62
|
# in bunny.
|
63
63
|
attr_accessor :channel_max
|
64
64
|
|
65
|
+
# Lazy queues have the advantage of consuming a lot less memory on the broker. For backwards
|
66
|
+
# compatibility, they are disabled by default.
|
67
|
+
attr_accessor :lazy_queues_enabled
|
68
|
+
alias_method :lazy_queues_enabled?, :lazy_queues_enabled
|
69
|
+
|
65
70
|
# In contrast to RabbitMQ 2.x, RabbitMQ 3.x preserves message order when requeing a message. This can lead to
|
66
71
|
# throughput degradation (when rejected messages block the processing of other messages
|
67
72
|
# at the head of the queue) in some cases.
|
@@ -89,6 +94,11 @@ module Beetle
|
|
89
94
|
# consider this a highly experimental feature for now.
|
90
95
|
attr_accessor :publishing_timeout
|
91
96
|
|
97
|
+
# the connect/disconnect timeout in seconds for the publishing connection
|
98
|
+
# (defaults to <tt>5</tt>).
|
99
|
+
# consider this a highly experimental feature for now.
|
100
|
+
attr_accessor :publisher_connect_timeout
|
101
|
+
|
92
102
|
# Prefetch count for subscribers (defaults to 1). Setting this higher
|
93
103
|
# than 1 can potentially increase throughput, but comes at the cost of
|
94
104
|
# decreased parallelism.
|
@@ -138,7 +148,10 @@ module Beetle
|
|
138
148
|
self.dead_lettering_msg_ttl = 1000 #1 second
|
139
149
|
self.dead_lettering_read_timeout = 3 #3 seconds
|
140
150
|
|
151
|
+
self.lazy_queues_enabled = false
|
152
|
+
|
141
153
|
self.publishing_timeout = 0
|
154
|
+
self.publisher_connect_timeout = 5
|
142
155
|
self.tmpdir = "/tmp"
|
143
156
|
|
144
157
|
self.log_file = STDOUT
|
@@ -10,54 +10,64 @@ module Beetle
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def bind_dead_letter_queues!(channel, servers, target_queue, creation_keys = {})
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
channel.queue(dead_letter_queue_name, creation_keys)
|
19
|
-
|
20
|
-
logger.debug("Beetle: setting #{dead_letter_queue_name} as dead letter queue of #{target_queue} on all servers")
|
21
|
-
set_dead_letter_policies!(servers, target_queue)
|
13
|
+
if @config.dead_lettering_enabled?
|
14
|
+
dead_letter_queue_name = dead_letter_queue_name(target_queue)
|
15
|
+
logger.debug("Beetle: creating dead letter queue #{dead_letter_queue_name} with opts: #{creation_keys.inspect}")
|
16
|
+
channel.queue(dead_letter_queue_name, creation_keys)
|
17
|
+
end
|
22
18
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
19
|
+
if @config.dead_lettering_enabled?
|
20
|
+
logger.debug("Beetle: setting #{dead_letter_queue_name} as dead letter queue of #{target_queue} on all servers")
|
21
|
+
end
|
22
|
+
set_queue_policies!(servers, target_queue)
|
23
|
+
|
24
|
+
if @config.dead_lettering_enabled?
|
25
|
+
logger.debug("Beetle: setting #{target_queue} as dead letter queue of #{dead_letter_queue_name} on all servers")
|
26
|
+
set_queue_policies!(
|
27
|
+
servers,
|
28
|
+
dead_letter_queue_name,
|
29
|
+
:message_ttl => @config.dead_lettering_msg_ttl,
|
30
|
+
:routing_key => target_queue
|
31
|
+
)
|
32
|
+
end
|
30
33
|
end
|
31
34
|
|
32
|
-
def
|
33
|
-
servers.each { |server|
|
35
|
+
def set_queue_policies!(servers, queue_name, options={})
|
36
|
+
servers.each { |server| set_queue_policy!(server, queue_name, options) }
|
34
37
|
end
|
35
38
|
|
36
|
-
def
|
39
|
+
def set_queue_policy!(server, queue_name, options={})
|
37
40
|
raise ArgumentError.new("server missing") if server.blank?
|
38
41
|
raise ArgumentError.new("queue name missing") if queue_name.blank?
|
39
42
|
|
43
|
+
return unless @config.dead_lettering_enabled? || @config.lazy_queues_enabled?
|
44
|
+
|
40
45
|
vhost = CGI.escape(@config.vhost)
|
41
46
|
request_url = URI("http://#{server}/api/policies/#{vhost}/#{queue_name}_policy")
|
42
47
|
request = Net::HTTP::Put.new(request_url)
|
43
48
|
|
49
|
+
# set up queue policy
|
50
|
+
definition = {}
|
51
|
+
if @config.dead_lettering_enabled?
|
52
|
+
definition["dead-letter-routing-key"] = dead_letter_routing_key(queue_name, options)
|
53
|
+
definition["dead-letter-exchange"] = ""
|
54
|
+
definition["message-ttl"] = options[:message_ttl] if options[:message_ttl]
|
55
|
+
end
|
56
|
+
|
57
|
+
definition["queue-mode"] = "lazy" if @config.lazy_queues_enabled?
|
58
|
+
|
44
59
|
request_body = {
|
45
60
|
"pattern" => "^#{queue_name}$",
|
46
61
|
"priority" => 1,
|
47
62
|
"apply-to" => "queues",
|
48
|
-
"definition" =>
|
49
|
-
"dead-letter-routing-key" => dead_letter_routing_key(queue_name, options),
|
50
|
-
"dead-letter-exchange" => ""
|
51
|
-
}
|
63
|
+
"definition" => definition,
|
52
64
|
}
|
53
65
|
|
54
|
-
request_body["definition"].merge!("message-ttl" => options[:message_ttl]) if options[:message_ttl]
|
55
|
-
|
56
66
|
response = run_rabbit_http_request(request_url, request) do |http|
|
57
67
|
http.request(request, request_body.to_json)
|
58
68
|
end
|
59
69
|
|
60
|
-
|
70
|
+
unless %w(200 201 204).include?(response.code)
|
61
71
|
log_error("Failed to create policy for queue #{queue_name}", response)
|
62
72
|
raise FailedRabbitRequest.new("Could not create policy")
|
63
73
|
end
|
@@ -142,13 +142,30 @@ module Beetle
|
|
142
142
|
@last_time_master_file_changed != File.mtime(@config.redis_server)
|
143
143
|
end
|
144
144
|
|
145
|
-
# set current redis master from server:port string contained in the redis master
|
145
|
+
# set current redis master from server:port string contained in the redis master for our system
|
146
146
|
def set_current_redis_master_from_master_file
|
147
147
|
@last_time_master_file_changed = File.mtime(@config.redis_server)
|
148
|
-
server_string = read_master_file
|
148
|
+
server_string = extract_redis_master(read_master_file)
|
149
149
|
@current_master = !server_string.blank? ? Redis.from_server_string(server_string, :db => @config.redis_db) : nil
|
150
150
|
end
|
151
151
|
|
152
|
+
# extract redis master from file content and return the server for our system
|
153
|
+
def extract_redis_master(text)
|
154
|
+
system_name = @config.system_name
|
155
|
+
redis_master = ""
|
156
|
+
text.each_line do |line|
|
157
|
+
parts = line.split('/')
|
158
|
+
case parts.size
|
159
|
+
when 1
|
160
|
+
redis_master = parts[0]
|
161
|
+
when 2
|
162
|
+
name, master = parts
|
163
|
+
redis_master = master if name == system_name
|
164
|
+
end
|
165
|
+
end
|
166
|
+
redis_master
|
167
|
+
end
|
168
|
+
|
152
169
|
# server:port string from the redis master file
|
153
170
|
def read_master_file
|
154
171
|
File.read(@config.redis_server).chomp
|
data/lib/beetle/message.rb
CHANGED
@@ -146,8 +146,16 @@ module Beetle
|
|
146
146
|
end
|
147
147
|
|
148
148
|
# generate uuid for publishing
|
149
|
-
|
150
|
-
|
149
|
+
begin
|
150
|
+
require "uuid4r"
|
151
|
+
def self.generate_uuid
|
152
|
+
UUID4R::uuid(4)
|
153
|
+
end
|
154
|
+
rescue LoadError
|
155
|
+
require "securerandom"
|
156
|
+
def self.generate_uuid
|
157
|
+
SecureRandom.uuid
|
158
|
+
end
|
151
159
|
end
|
152
160
|
|
153
161
|
# whether the publisher has tried sending this message to two servers
|
data/lib/beetle/publisher.rb
CHANGED
@@ -159,15 +159,16 @@ module Beetle
|
|
159
159
|
|
160
160
|
def new_bunny
|
161
161
|
b = Bunny.new(
|
162
|
-
:host
|
163
|
-
:port
|
164
|
-
:logging
|
165
|
-
:user
|
166
|
-
:pass
|
167
|
-
:vhost
|
168
|
-
:frame_max
|
169
|
-
:channel_max
|
170
|
-
:socket_timeout
|
162
|
+
:host => current_host,
|
163
|
+
:port => current_port,
|
164
|
+
:logging => !!@options[:logging],
|
165
|
+
:user => @client.config.user,
|
166
|
+
:pass => @client.config.password,
|
167
|
+
:vhost => @client.config.vhost,
|
168
|
+
:frame_max => @client.config.frame_max,
|
169
|
+
:channel_max => @client.config.channel_max,
|
170
|
+
:socket_timeout => @client.config.publishing_timeout,
|
171
|
+
:connect_timeout => @client.config.publisher_connect_timeout,
|
171
172
|
:spec => '09')
|
172
173
|
b.start
|
173
174
|
b
|
@@ -224,7 +225,8 @@ module Beetle
|
|
224
225
|
|
225
226
|
def stop!(exception=nil)
|
226
227
|
return unless bunny?
|
227
|
-
|
228
|
+
timeout = @client.config.publishing_timeout + @client.config.publisher_connect_timeout + 1
|
229
|
+
Beetle::Timer.timeout(timeout) do
|
228
230
|
logger.debug "Beetle: closing connection from publisher to #{server}"
|
229
231
|
if exception
|
230
232
|
bunny.__send__ :close_socket
|
data/lib/beetle/version.rb
CHANGED
data/script/console
CHANGED
@@ -10,16 +10,16 @@ OptionParser.new do |opt|
|
|
10
10
|
end
|
11
11
|
|
12
12
|
libs = " -r irb/completion"
|
13
|
-
libs << %( -r
|
13
|
+
libs << %( -r rubygems)
|
14
14
|
libs << %( -r #{File.expand_path("../../lib/beetle.rb",__FILE__)})
|
15
15
|
|
16
16
|
if options[:debugger]
|
17
17
|
begin
|
18
|
-
require '
|
19
|
-
libs << " -r
|
18
|
+
require 'byebug'
|
19
|
+
libs << " -r byebug"
|
20
20
|
puts "=> Debugger enabled"
|
21
21
|
rescue Exception
|
22
|
-
puts "You need to install
|
22
|
+
puts "You need to install byebug to run the console in debugging mode. With gems, use 'gem install byebug'"
|
23
23
|
exit
|
24
24
|
end
|
25
25
|
end
|
@@ -3,18 +3,20 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
|
3
3
|
module Beetle
|
4
4
|
class SetDeadLetteringsTest < Minitest::Test
|
5
5
|
def setup
|
6
|
-
@
|
6
|
+
@config = Configuration.new
|
7
|
+
@dead_lettering = DeadLettering.new(@config)
|
8
|
+
@config.dead_lettering_enabled = true
|
7
9
|
end
|
8
10
|
|
9
11
|
test "creates a dead letter queue for each server" do
|
10
12
|
servers = %w(a b)
|
11
13
|
|
12
|
-
@dead_lettering.expects(:
|
14
|
+
@dead_lettering.expects(:set_queue_policy!).
|
13
15
|
with("a", "QUEUE_NAME", :message_ttl => 10000)
|
14
|
-
@dead_lettering.expects(:
|
16
|
+
@dead_lettering.expects(:set_queue_policy!).
|
15
17
|
with("b", "QUEUE_NAME", :message_ttl => 10000)
|
16
18
|
|
17
|
-
@dead_lettering.
|
19
|
+
@dead_lettering.set_queue_policies!(servers, "QUEUE_NAME", :message_ttl => 10000)
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
@@ -25,21 +27,22 @@ module Beetle
|
|
25
27
|
@config = Configuration.new
|
26
28
|
@config.logger = Logger.new("/dev/null")
|
27
29
|
@dead_lettering = DeadLettering.new(@config)
|
30
|
+
@config.dead_lettering_enabled = true
|
28
31
|
end
|
29
32
|
|
30
33
|
test "raises exception when queue name wasn't specified" do
|
31
34
|
assert_raises ArgumentError do
|
32
|
-
@dead_lettering.
|
35
|
+
@dead_lettering.set_queue_policy!(@server, "")
|
33
36
|
end
|
34
37
|
end
|
35
38
|
|
36
39
|
test "raises exception when no server was specified" do
|
37
40
|
assert_raises ArgumentError do
|
38
|
-
@dead_lettering.
|
41
|
+
@dead_lettering.set_queue_policy!("", @queue_name)
|
39
42
|
end
|
40
43
|
end
|
41
44
|
|
42
|
-
test "creates a policy by posting to the rabbitmq" do
|
45
|
+
test "creates a policy by posting to the rabbitmq if dead lettering is enabled" do
|
43
46
|
stub_request(:put, "http://localhost:15672/api/policies/%2F/QUEUE_NAME_policy")
|
44
47
|
.with(basic_auth: ['guest', 'guest'])
|
45
48
|
.with(:body => {
|
@@ -52,7 +55,24 @@ module Beetle
|
|
52
55
|
}}.to_json)
|
53
56
|
.to_return(:status => 204)
|
54
57
|
|
55
|
-
@dead_lettering.
|
58
|
+
@dead_lettering.set_queue_policy!(@server, @queue_name)
|
59
|
+
end
|
60
|
+
|
61
|
+
test "creates a policy by posting to the rabbitmq if lazy queues are enabled" do
|
62
|
+
@config.lazy_queues_enabled = true
|
63
|
+
@config.dead_lettering_enabled = false
|
64
|
+
stub_request(:put, "http://localhost:15672/api/policies/%2F/QUEUE_NAME_policy")
|
65
|
+
.with(basic_auth: ['guest', 'guest'])
|
66
|
+
.with(:body => {
|
67
|
+
"pattern" => "^QUEUE_NAME$",
|
68
|
+
"priority" => 1,
|
69
|
+
"apply-to" => "queues",
|
70
|
+
"definition" => {
|
71
|
+
"queue-mode" => "lazy"
|
72
|
+
}}.to_json)
|
73
|
+
.to_return(:status => 204)
|
74
|
+
|
75
|
+
@dead_lettering.set_queue_policy!(@server, @queue_name)
|
56
76
|
end
|
57
77
|
|
58
78
|
test "raises exception when policy couldn't successfully be created" do
|
@@ -61,7 +81,7 @@ module Beetle
|
|
61
81
|
.to_return(:status => [405])
|
62
82
|
|
63
83
|
assert_raises DeadLettering::FailedRabbitRequest do
|
64
|
-
@dead_lettering.
|
84
|
+
@dead_lettering.set_queue_policy!(@server, @queue_name)
|
65
85
|
end
|
66
86
|
end
|
67
87
|
|
@@ -79,7 +99,7 @@ module Beetle
|
|
79
99
|
}}.to_json)
|
80
100
|
.to_return(:status => 204)
|
81
101
|
|
82
|
-
@dead_lettering.
|
102
|
+
@dead_lettering.set_queue_policy!(@server, @queue_name, :message_ttl => 10000)
|
83
103
|
end
|
84
104
|
|
85
105
|
test "properly encodes the vhost from the configuration" do
|
@@ -97,7 +117,7 @@ module Beetle
|
|
97
117
|
|
98
118
|
@config.vhost = "foo/"
|
99
119
|
|
100
|
-
@dead_lettering.
|
120
|
+
@dead_lettering.set_queue_policy!(@server, @queue_name)
|
101
121
|
end
|
102
122
|
end
|
103
123
|
|
@@ -110,9 +130,10 @@ module Beetle
|
|
110
130
|
@servers = ["localhost:55672"]
|
111
131
|
end
|
112
132
|
|
113
|
-
test "is
|
133
|
+
test "is does not call out to rabbit if neither dead lettering nor lazy queues are enabled" do
|
134
|
+
@config.dead_lettering_enabled = false
|
114
135
|
channel = stub('channel')
|
115
|
-
@dead_lettering.expects(:
|
136
|
+
@dead_lettering.expects(:run_rabbit_http_request).never
|
116
137
|
@dead_lettering.bind_dead_letter_queues!(channel, @servers, @queue_name)
|
117
138
|
end
|
118
139
|
|
@@ -122,8 +143,8 @@ module Beetle
|
|
122
143
|
channel = stub('channel')
|
123
144
|
|
124
145
|
channel.expects(:queue).with("#{@queue_name}_dead_letter", {})
|
125
|
-
@dead_lettering.expects(:
|
126
|
-
@dead_lettering.expects(:
|
146
|
+
@dead_lettering.expects(:set_queue_policies!).with(@servers, @queue_name)
|
147
|
+
@dead_lettering.expects(:set_queue_policies!).with(@servers, "#{@queue_name}_dead_letter",
|
127
148
|
:routing_key => @queue_name,
|
128
149
|
:message_ttl => 1000
|
129
150
|
)
|
@@ -43,6 +43,7 @@ module Beetle
|
|
43
43
|
class RedisServerFileTest < Minitest::Test
|
44
44
|
def setup
|
45
45
|
@original_redis_server = Beetle.config.redis_server
|
46
|
+
@original_system_name = Beetle.config.system_name
|
46
47
|
@store = DeduplicationStore.new
|
47
48
|
@server_string = "my_test_host_from_file:6379"
|
48
49
|
Beetle.config.redis_server = redis_test_master_file(@server_string)
|
@@ -50,6 +51,7 @@ module Beetle
|
|
50
51
|
|
51
52
|
def teardown
|
52
53
|
Beetle.config.redis_server = @original_redis_server
|
54
|
+
Beetle.config.system_name = @original_system_name
|
53
55
|
end
|
54
56
|
|
55
57
|
test "redis should match the redis master file" do
|
@@ -71,6 +73,17 @@ module Beetle
|
|
71
73
|
assert_raises(Errno::ENOENT) { @store.redis_master_from_master_file }
|
72
74
|
end
|
73
75
|
|
76
|
+
test "should retrieve the redis master for the configured system name if the master file contains a mapping for it" do
|
77
|
+
redis_test_master_file("blabber/localhost:2\nblubber/localhost:1")
|
78
|
+
Beetle.config.system_name = "blubber"
|
79
|
+
assert_equal "localhost:1", @store.redis.server
|
80
|
+
end
|
81
|
+
|
82
|
+
test "should retrieve the redis master for the default system name if the master file contains a simple host:port entry" do
|
83
|
+
redis_test_master_file("localhost:2\nblubber/localhost:1")
|
84
|
+
assert_equal "localhost:2", @store.redis.server
|
85
|
+
end
|
86
|
+
|
74
87
|
private
|
75
88
|
def redis_test_master_file(server_string)
|
76
89
|
tmp_dir = File.expand_path("../../../tmp", __FILE__)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: beetle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stefan Kaes
|
@@ -12,36 +12,22 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2019-02-
|
15
|
+
date: 2019-02-28 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
|
-
- !ruby/object:Gem::Dependency
|
18
|
-
name: uuid4r
|
19
|
-
requirement: !ruby/object:Gem::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">="
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 0.1.2
|
24
|
-
type: :runtime
|
25
|
-
prerelease: false
|
26
|
-
version_requirements: !ruby/object:Gem::Requirement
|
27
|
-
requirements:
|
28
|
-
- - ">="
|
29
|
-
- !ruby/object:Gem::Version
|
30
|
-
version: 0.1.2
|
31
17
|
- !ruby/object:Gem::Dependency
|
32
18
|
name: bunny
|
33
19
|
requirement: !ruby/object:Gem::Requirement
|
34
20
|
requirements:
|
35
21
|
- - "~>"
|
36
22
|
- !ruby/object:Gem::Version
|
37
|
-
version: 0.7.
|
23
|
+
version: 0.7.12
|
38
24
|
type: :runtime
|
39
25
|
prerelease: false
|
40
26
|
version_requirements: !ruby/object:Gem::Requirement
|
41
27
|
requirements:
|
42
28
|
- - "~>"
|
43
29
|
- !ruby/object:Gem::Version
|
44
|
-
version: 0.7.
|
30
|
+
version: 0.7.12
|
45
31
|
- !ruby/object:Gem::Dependency
|
46
32
|
name: redis
|
47
33
|
requirement: !ruby/object:Gem::Requirement
|
@@ -112,6 +98,20 @@ dependencies:
|
|
112
98
|
- - ">="
|
113
99
|
- !ruby/object:Gem::Version
|
114
100
|
version: 2.3.4
|
101
|
+
- !ruby/object:Gem::Dependency
|
102
|
+
name: uuid4r
|
103
|
+
requirement: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: 0.1.2
|
108
|
+
type: :development
|
109
|
+
prerelease: false
|
110
|
+
version_requirements: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: 0.1.2
|
115
115
|
- !ruby/object:Gem::Dependency
|
116
116
|
name: activerecord
|
117
117
|
requirement: !ruby/object:Gem::Requirement
|
@@ -328,7 +328,6 @@ files:
|
|
328
328
|
- examples/redundant.rb
|
329
329
|
- examples/rpc.rb
|
330
330
|
- examples/simple.rb
|
331
|
-
- features/#redis_auto_failover.feature#
|
332
331
|
- features/README.rdoc
|
333
332
|
- features/redis_auto_failover.feature
|
334
333
|
- features/step_definitions/redis_auto_failover_steps.rb
|
@@ -351,7 +350,6 @@ files:
|
|
351
350
|
- lib/beetle/publisher.rb
|
352
351
|
- lib/beetle/r_c.rb
|
353
352
|
- lib/beetle/redis_ext.rb
|
354
|
-
- lib/beetle/redis_master_file.rb
|
355
353
|
- lib/beetle/subscriber.rb
|
356
354
|
- lib/beetle/version.rb
|
357
355
|
- script/console
|
@@ -369,13 +367,13 @@ files:
|
|
369
367
|
- test/beetle/publisher_test.rb
|
370
368
|
- test/beetle/r_c_test.rb
|
371
369
|
- test/beetle/redis_ext_test.rb
|
372
|
-
- test/beetle/redis_master_file_test.rb
|
373
370
|
- test/beetle/subscriber_test.rb
|
374
371
|
- test/beetle_test.rb
|
375
372
|
- test/test_helper.rb
|
376
373
|
homepage: https://xing.github.com/beetle/
|
377
374
|
licenses: []
|
378
|
-
metadata:
|
375
|
+
metadata:
|
376
|
+
changelog_uri: https://github.com/xing/beetle/blob/master/RELEASE_NOTES.rdoc
|
379
377
|
post_install_message:
|
380
378
|
rdoc_options:
|
381
379
|
- "--charset=UTF-8"
|
@@ -403,7 +401,6 @@ test_files:
|
|
403
401
|
- test/beetle/deduplication_store_test.rb
|
404
402
|
- test/beetle/handler_test.rb
|
405
403
|
- test/beetle/beetle_test.rb
|
406
|
-
- test/beetle/redis_master_file_test.rb
|
407
404
|
- test/beetle/configuration_test.rb
|
408
405
|
- test/beetle/subscriber_test.rb
|
409
406
|
- test/beetle/message/settings_test.rb
|
@@ -1,165 +0,0 @@
|
|
1
|
-
Feature: Redis auto failover
|
2
|
-
In order to eliminate a single point of failure
|
3
|
-
Beetle handlers should automatically switch to a new redis master in case of a redis master failure
|
4
|
-
|
5
|
-
Background:
|
6
|
-
Given a redis server "redis-1" exists as master
|
7
|
-
And a redis server "redis-2" exists as slave of "redis-1"
|
8
|
-
|
9
|
-
Scenario: Successful redis master switch
|
10
|
-
Given a redis configuration server using redis servers "redis-1,redis-2" with clients "rc-client-1,rc-client-2" exists
|
11
|
-
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
12
|
-
And a redis configuration client "rc-client-2" using redis servers "redis-1,redis-2" exists
|
13
|
-
And a beetle handler using the redis-master file from "rc-client-1" exists
|
14
|
-
And redis server "redis-1" is down
|
15
|
-
And the retry timeout for the redis master check is reached
|
16
|
-
Then a system notification for "redis-1" not being available should be sent
|
17
|
-
And the role of redis server "redis-2" should be "master"
|
18
|
-
And the redis master file of the redis configuration server should contain "redis-2"
|
19
|
-
And the redis master of "rc-client-1" should be "redis-2"
|
20
|
-
And the redis master of "rc-client-2" should be "redis-2"
|
21
|
-
And the redis master of the beetle handler should be "redis-2"
|
22
|
-
And a system notification for switching from "redis-1" to "redis-2" should be sent
|
23
|
-
Given a redis server "redis-1" exists as master
|
24
|
-
Then the role of redis server "redis-1" should be "slave"
|
25
|
-
|
26
|
-
Scenario: Not all redis configuration clients available (switch possible using confidence level 50)
|
27
|
-
Given a redis configuration server using redis servers "redis-1,redis-2" with clients "rc-client-1,rc-client-2" and confidence level "50" exists
|
28
|
-
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
29
|
-
And a beetle handler using the redis-master file from "rc-client-1" exists
|
30
|
-
And redis configuration client "rc-client-2" is down
|
31
|
-
And redis server "redis-1" is down
|
32
|
-
And the retry timeout for the redis master check is reached
|
33
|
-
Then a system notification for "redis-1" not being available should be sent
|
34
|
-
And the role of redis server "redis-2" should be "master"
|
35
|
-
And the redis master file of the redis configuration server should contain "redis-2"
|
36
|
-
And the redis master of "rc-client-1" should be "redis-2"
|
37
|
-
And the redis master of the beetle handler should be "redis-2"
|
38
|
-
And a system notification for switching from "redis-1" to "redis-2" should be sent
|
39
|
-
Given a redis server "redis-1" exists as master
|
40
|
-
Then the role of redis server "redis-1" should be "slave"
|
41
|
-
|
42
|
-
Scenario: Redis master only temporarily down (no switch necessary)
|
43
|
-
Given a redis configuration server using redis servers "redis-1,redis-2" with clients "rc-client-1,rc-client-2" exists
|
44
|
-
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
45
|
-
And a redis configuration client "rc-client-2" using redis servers "redis-1,redis-2" exists
|
46
|
-
And a beetle handler using the redis-master file from "rc-client-1" exists
|
47
|
-
And redis server "redis-1" is down for less seconds than the retry timeout for the redis master check
|
48
|
-
And the retry timeout for the redis master check is reached
|
49
|
-
Then the role of redis server "redis-1" should be "master"
|
50
|
-
Then the role of redis server "redis-2" should be "slave"
|
51
|
-
And the redis master of "rc-client-1" should be "redis-1"
|
52
|
-
And the redis master of "rc-client-2" should be "redis-1"
|
53
|
-
And the redis master of the beetle handler should be "redis-1"
|
54
|
-
|
55
|
-
Scenario: Not all redis configuration clients available (no switch possible)
|
56
|
-
Given a redis configuration server using redis servers "redis-1,redis-2" with clients "rc-client-1,rc-client-2" exists
|
57
|
-
And redis server "redis-1" is down
|
58
|
-
And the retry timeout for the redis master check is reached
|
59
|
-
Then the role of redis server "redis-2" should be "slave"
|
60
|
-
|
61
|
-
Scenario: No redis slave available to become new master (no switch possible)
|
62
|
-
Given a redis configuration server using redis servers "redis-1,redis-2" with clients "rc-client-1,rc-client-2" exists
|
63
|
-
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
64
|
-
And a redis configuration client "rc-client-2" using redis servers "redis-1,redis-2" exists
|
65
|
-
And redis server "redis-1" is down
|
66
|
-
And redis server "redis-2" is down
|
67
|
-
And the retry timeout for the redis master check is reached
|
68
|
-
Then the redis master of "rc-client-1" should be "redis-1"
|
69
|
-
And the redis master of "rc-client-2" should be "redis-1"
|
70
|
-
And a system notification for no slave available to become new master should be sent
|
71
|
-
|
72
|
-
Scenario: Redis configuration client starts while no redis master available
|
73
|
-
Given redis server "redis-1" is down
|
74
|
-
And redis server "redis-2" is down
|
75
|
-
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
76
|
-
And the retry timeout for the redis master determination is reached
|
77
|
-
Then the redis master of "rc-client-1" should be undefined
|
78
|
-
|
79
|
-
Scenario: Redis configuration client starts while no redis master available but master file exists
|
80
|
-
Given redis server "redis-1" is down
|
81
|
-
And redis server "redis-2" is down
|
82
|
-
And an old redis master file for "rc-client-1" with master "redis-1" exists
|
83
|
-
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
84
|
-
And the retry timeout for the redis master determination is reached
|
85
|
-
Then the redis master of "rc-client-1" should be undefined
|
86
|
-
|
87
|
-
Scenario: Redis configuration client starts while both redis servers are master
|
88
|
-
Given redis server "redis-2" is master
|
89
|
-
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
90
|
-
Then the redis master of "rc-client-1" should be undefined
|
91
|
-
|
92
|
-
Scenario: Redis configuration client starts while both redis servers are master but master file exists
|
93
|
-
Given redis server "redis-2" is master
|
94
|
-
And an old redis master file for "rc-client-1" with master "redis-1" exists
|
95
|
-
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
96
|
-
Then the redis master of "rc-client-1" should be "redis-1"
|
97
|
-
|
98
|
-
Scenario: Redis configuration client starts while both redis servers are slave
|
99
|
-
Given a redis server "redis-3" exists as master
|
100
|
-
And redis server "redis-1" is slave of "redis-3"
|
101
|
-
And redis server "redis-2" is slave of "redis-3"
|
102
|
-
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
103
|
-
Then the redis master of "rc-client-1" should be undefined
|
104
|
-
|
105
|
-
Scenario: Redis configuration client starts while both redis servers are slave but master file exists
|
106
|
-
Given a redis server "redis-3" exists as master
|
107
|
-
And redis server "redis-1" is slave of "redis-3"
|
108
|
-
And redis server "redis-2" is slave of "redis-3"
|
109
|
-
And an old redis master file for "rc-client-1" with master "redis-1" exists
|
110
|
-
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
111
|
-
Then the redis master of "rc-client-1" should be undefined
|
112
|
-
|
113
|
-
Scenario: Redis configuration client starts while there is a redis master but no slave
|
114
|
-
Given redis server "redis-2" is down
|
115
|
-
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
116
|
-
Then the redis master of "rc-client-1" should be undefined
|
117
|
-
|
118
|
-
Scenario: Redis configuration client starts while there is a redis master but no slave but master file exists
|
119
|
-
Given redis server "redis-2" is down
|
120
|
-
And an old redis master file for "rc-client-1" with master "redis-1" exists
|
121
|
-
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
122
|
-
Then the redis master of "rc-client-1" should be "redis-1"
|
123
|
-
|
124
|
-
Scenario: Redis configuation server should embed a http server
|
125
|
-
Given a redis configuration server using redis servers "redis-1,redis-2" with clients "rc-client-1,rc-client-2" exists
|
126
|
-
Then the redis configuration server should answer http requests
|
127
|
-
|
128
|
-
Scenario: Accelerated redis master switch when master is down
|
129
|
-
Given a redis configuration server using redis servers "redis-1,redis-2" with clients "rc-client-1,rc-client-2" exists
|
130
|
-
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
131
|
-
And a redis configuration client "rc-client-2" using redis servers "redis-1,redis-2" exists
|
132
|
-
And a beetle handler using the redis-master file from "rc-client-1" exists
|
133
|
-
And redis server "redis-1" is down
|
134
|
-
And an immediate master switch is initiated and responds with 201
|
135
|
-
Then a system notification for "redis-1" not being available should be sent
|
136
|
-
And the role of redis server "redis-2" should be "master"
|
137
|
-
And the redis master of "rc-client-1" should be "redis-2"
|
138
|
-
And the redis master of "rc-client-2" should be "redis-2"
|
139
|
-
And the redis master of the beetle handler should be "redis-2"
|
140
|
-
And a system notification for switching from "redis-1" to "redis-2" should be sent
|
141
|
-
Given a redis server "redis-1" exists as master
|
142
|
-
Then the role of redis server "redis-1" should be "slave"
|
143
|
-
|
144
|
-
Scenario: Accelerated redis master switch when master is up
|
145
|
-
Given a redis configuration server using redis servers "redis-1,redis-2" with clients "rc-client-1,rc-client-2" exists
|
146
|
-
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
147
|
-
And a redis configuration client "rc-client-2" using redis servers "redis-1,redis-2" exists
|
148
|
-
And a beetle handler using the redis-master file from "rc-client-1" exists
|
149
|
-
And an immediate master switch is initiated and responds with 200
|
150
|
-
Then the role of redis server "redis-1" should be "master"
|
151
|
-
And the redis master of "rc-client-1" should be "redis-1"
|
152
|
-
And the redis master of "rc-client-2" should be "redis-1"
|
153
|
-
And the redis master of the beetle handler should be "redis-1"
|
154
|
-
And the role of redis server "redis-2" should be "slave"
|
155
|
-
|
156
|
-
# Scenario: Running the system for a few seconds to perform manual testing
|
157
|
-
# Given a redis configuration server using redis servers "redis-1,redis-2" with clients "rc-client-1,rc-client-2" exists
|
158
|
-
# And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
159
|
-
# And a redis configuration client "rc-client-2" using redis servers "redis-1,redis-2" exists
|
160
|
-
# And a beetle handler using the redis-master file from "rc-client-1" exists
|
161
|
-
# And the redis master of "rc-client-1" should be "redis-1"
|
162
|
-
# And the redis master of "rc-client-2" should be "redis-1"
|
163
|
-
# And the redis master of the beetle handler should be "redis-1"
|
164
|
-
# And the role of redis server "redis-2" should be "slave"
|
165
|
-
# Then the system can run for a while without dying
|
@@ -1,30 +0,0 @@
|
|
1
|
-
module Beetle
|
2
|
-
module RedisMasterFile #:nodoc:
|
3
|
-
private
|
4
|
-
def master_file_exists?
|
5
|
-
File.exist?(master_file)
|
6
|
-
end
|
7
|
-
|
8
|
-
def redis_master_from_master_file
|
9
|
-
redis.find(read_redis_master_file)
|
10
|
-
end
|
11
|
-
|
12
|
-
def clear_redis_master_file
|
13
|
-
write_redis_master_file("")
|
14
|
-
end
|
15
|
-
|
16
|
-
def read_redis_master_file
|
17
|
-
File.read(master_file).chomp
|
18
|
-
end
|
19
|
-
|
20
|
-
def write_redis_master_file(redis_server_string)
|
21
|
-
logger.warn "Writing '#{redis_server_string}' to redis master file '#{master_file}'"
|
22
|
-
File.open(master_file, "w"){|f| f.puts redis_server_string }
|
23
|
-
end
|
24
|
-
|
25
|
-
def master_file
|
26
|
-
config.redis_server
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
-
|
3
|
-
module Beetle
|
4
|
-
|
5
|
-
class RedisMasterFileTest < Minitest::Test
|
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
|