beetle 0.2.3 → 0.2.5
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/MIT-LICENSE +20 -0
- data/REDIS_AUTO_FAILOVER.rdoc +124 -0
- data/RELEASE_NOTES.rdoc +50 -0
- data/Rakefile +113 -0
- data/beetle.gemspec +3 -3
- data/features/README.rdoc +23 -0
- data/features/redis_auto_failover.feature +105 -0
- data/features/step_definitions/redis_auto_failover_steps.rb +133 -0
- data/features/support/beetle_handler +32 -0
- data/features/support/env.rb +48 -0
- data/features/support/system_notification_logger +31 -0
- data/features/support/test_daemons/redis.conf.erb +189 -0
- data/features/support/test_daemons/redis.rb +186 -0
- data/features/support/test_daemons/redis_configuration_client.rb +64 -0
- data/features/support/test_daemons/redis_configuration_server.rb +52 -0
- data/lib/beetle/redis_configuration_server.rb +10 -0
- data/script/console +28 -0
- data/script/start_rabbit +29 -0
- data/test/beetle/deduplication_store_test.rb +3 -1
- data/test/beetle/redis_configuration_server_test.rb +18 -0
- metadata +25 -4
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 XING AG
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,124 @@
|
|
1
|
+
= Automatic Redis Failover for Beetle
|
2
|
+
|
3
|
+
== Introduction
|
4
|
+
|
5
|
+
Redis is used as the persistence layer in the AMQP message deduplication
|
6
|
+
process. Because it is such a critical piece in our infrastructure, it is
|
7
|
+
essential that a failure of this service is as unlikely as possible. As our
|
8
|
+
AMQP workers are working in a highly distributed manner, all accessing the same
|
9
|
+
Redis server, a automatic failover to another Redis server has to be very
|
10
|
+
defensive and ensure that every worker in the system will switch to the new
|
11
|
+
server at the same time. If the new server would not get accepted from every
|
12
|
+
worker, a switch would not be possible. This ensures that even in the case of a
|
13
|
+
partitioned network it is impossible that two different workers use two
|
14
|
+
different Redis servers for message deduplication.
|
15
|
+
|
16
|
+
== Our goals
|
17
|
+
|
18
|
+
* opt-in, no need to use the redis-failover solution
|
19
|
+
* no single point of failure
|
20
|
+
* automatic switch in case of redis-master failure
|
21
|
+
* switch should not cause inconsistent data on the redis servers
|
22
|
+
* workers should be able to determine the current redis-master without asking
|
23
|
+
another process (as long as the redis servers are working)
|
24
|
+
|
25
|
+
== How it works
|
26
|
+
|
27
|
+
To ensure consistency, a service (the Redis Configuration Server - RCS) is
|
28
|
+
constantly checking the availability and configuration of the currently
|
29
|
+
configured Redis master server. If this service detects that the Redis master
|
30
|
+
is no longer available, it tries to find an alternative server (one of the
|
31
|
+
slaves) which could be promoted to be the new Redis master.
|
32
|
+
|
33
|
+
On every worker server runs another daemon, the Redis Configuration Client
|
34
|
+
(RCC) which listens to messages sent by the RCS.
|
35
|
+
|
36
|
+
If the RCS finds another potential Redis Master, it sends out a message to see
|
37
|
+
if all known RCCs are still available (once again to eliminate the risk of a
|
38
|
+
partitioned network) and if they agree to the master switch.
|
39
|
+
|
40
|
+
If all RCCs have answered to that message, the RCS sends out a message which
|
41
|
+
tells the RCCs to invalidate the current master.
|
42
|
+
|
43
|
+
This happens by deleting the contents of a special file which is used
|
44
|
+
by the workers to store the current Redis master (the content of that file is
|
45
|
+
the hostname:port of the currently active Redis master). By doing that, it is
|
46
|
+
ensured that no operations are done to the old Redis master server anymore, because the
|
47
|
+
AMQP workers check this file's mtime and reads its contents in case that the
|
48
|
+
file changed, before every Redis operation. When the file has been emptied, the
|
49
|
+
RCCs respond to the "invalidate" message of the RCS. When all RCCs have
|
50
|
+
responded, the RCS knows for sure that it is safe to switch the Redis master
|
51
|
+
now. It sends a "reconfigure" message with the new Redis master hostname:port
|
52
|
+
to the RCCs, which then write that value into their redis master file.
|
53
|
+
|
54
|
+
Additionally, the RCS sends reconfigure messages with the current Redis master
|
55
|
+
periodically, to allow new RCCs to pick up the current master. Plus it turns
|
56
|
+
all other redis servers into slaves of the current master.
|
57
|
+
|
58
|
+
=== Prerequisites
|
59
|
+
|
60
|
+
* one redis-configuration-server process ("RCS", on one server), one redis-configuration-client process ("RCC") on every worker server
|
61
|
+
* the RCS knows about all possible RCCs using a list of client ids
|
62
|
+
* the RCS and RCCs exchange messages via a "system queue"
|
63
|
+
|
64
|
+
=== Flow of actions
|
65
|
+
|
66
|
+
* on startup, an RCC can consult its redis master file to determine the current master without the help of the RCS by checking that it's still a master (or wait for the periodic reconfigure message with the current master from the RCS)
|
67
|
+
* when the RCS finds the master to be down, it will retry a couple of times before starting a reconfiguration round
|
68
|
+
* the RCS sends all RCCs a "ping" message to check if every client is there and able to to answer
|
69
|
+
* the RCCs acknowledge via a "pong" message if they can confirm the current master to be unavailable
|
70
|
+
* the RCS waits for *all* RCCs to reply via pong
|
71
|
+
* the RCS tells all RCCs to stop using the master by sending an "invalidate" message
|
72
|
+
* the RCCs acknowledge via an "invalidated" message if they can still confirm the current master to be unavailable
|
73
|
+
* the RCS waits for *all* RCCs to acknowledge the invalidation
|
74
|
+
* the RCS promotes the former slave to become the new master (by sending SLAVEOF no one)
|
75
|
+
* the RCS sends a "reconfigure" message containing the new master to every RCC
|
76
|
+
* the RCCs write the new master to their redis master file
|
77
|
+
|
78
|
+
=== Configuration
|
79
|
+
|
80
|
+
See Beetle::Configuration for setting redis configuration server and client options.
|
81
|
+
|
82
|
+
Please note:
|
83
|
+
Beetle::Configuration#redis_server must be a file path (not a redis host:port string) to use the redis failover. The RCS and RCCs store the current redis master in that file, and the handlers read from it.
|
84
|
+
|
85
|
+
== How to use it
|
86
|
+
|
87
|
+
This example uses two worker servers, identified by rcc-1 and rcc-2.
|
88
|
+
|
89
|
+
Please note:
|
90
|
+
All command line options can also be given as a yaml configuration file via the --config-file option.
|
91
|
+
|
92
|
+
=== On one server
|
93
|
+
|
94
|
+
Start the Redis Configuration Server:
|
95
|
+
|
96
|
+
beetle configuration_server start -- --redis-servers redis-1:6379,redis-2:6379 --client-ids rcc-1,rcc-2
|
97
|
+
|
98
|
+
Get help for starting/stopping the server:
|
99
|
+
|
100
|
+
beetle configuration_server -h
|
101
|
+
|
102
|
+
Get help for server options:
|
103
|
+
|
104
|
+
beetle configuration_server start -- -h
|
105
|
+
|
106
|
+
=== On every worker server
|
107
|
+
|
108
|
+
Start the Redis Configuration Client:
|
109
|
+
|
110
|
+
On first worker server:
|
111
|
+
|
112
|
+
beetle configuration_client start -- --client-id rcc-1
|
113
|
+
|
114
|
+
On second worker server:
|
115
|
+
|
116
|
+
beetle configuration_client start -- --client-id rcc-2
|
117
|
+
|
118
|
+
Get help for starting/stopping the client:
|
119
|
+
|
120
|
+
beetle configuration_client -h
|
121
|
+
|
122
|
+
Get help for client options:
|
123
|
+
|
124
|
+
beetle configuration_client start -- -h
|
data/RELEASE_NOTES.rdoc
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
= Release Notes
|
2
|
+
|
3
|
+
== Version 0.2.5
|
4
|
+
|
5
|
+
Added missing files to gem and rdoc
|
6
|
+
|
7
|
+
== Version 0.2.4
|
8
|
+
|
9
|
+
Log and send a system notification when pong message from unknown client received.
|
10
|
+
|
11
|
+
== Version 0.2.2
|
12
|
+
|
13
|
+
Patch release which upgrades to redis-rb 2.0.4. This enables us to drop our redis monkey
|
14
|
+
patch which enabled connection timeouts for earlier redis versions. Note that earlier
|
15
|
+
Beetle versions are not compatible with redis 2.0.4.
|
16
|
+
|
17
|
+
== Version 0.2.1
|
18
|
+
|
19
|
+
Improved error message when no rabbitmq broker is available.
|
20
|
+
|
21
|
+
== Version 0.2
|
22
|
+
|
23
|
+
This version adds support for automatic redis deduplication store failover (see separate
|
24
|
+
file REDIS_AUTO_FAILOVER.rdoc).
|
25
|
+
|
26
|
+
=== User visible changes
|
27
|
+
|
28
|
+
* it's possible to register auto deleted queues and exchanges
|
29
|
+
* Beetle::Client#configure returns self in order to simplify client setup
|
30
|
+
* it's possible to trace specific messages (see Beetle::Client#trace)
|
31
|
+
* default message handler timeout is 10 minutes now
|
32
|
+
* system wide configuration values can be specified via a yml formatted configuration
|
33
|
+
file (Beetle::Configuration#config_file)
|
34
|
+
* the config value redis_server specifies either a single server or a file path (used
|
35
|
+
by the automatic redis failover logic)
|
36
|
+
|
37
|
+
=== Fugs Bixed
|
38
|
+
|
39
|
+
* handle active_support seconds notation for handler timeouts correctly
|
40
|
+
* error handler was erroneously called for expired messages
|
41
|
+
* subscribers would block when some non beetle process posts an undecodable message
|
42
|
+
|
43
|
+
=== Gem Dependency Changes
|
44
|
+
|
45
|
+
* redis needs to be at least version 2.0.3
|
46
|
+
* we make use of the SystemTimer gem for ruby 1.8.7
|
47
|
+
|
48
|
+
== Version 0.1
|
49
|
+
|
50
|
+
Initial Release
|
data/Rakefile
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rcov/rcovtask'
|
4
|
+
require 'cucumber/rake/task'
|
5
|
+
|
6
|
+
# 1.8/1.9 compatible way of loading lib/beetle.rb
|
7
|
+
$:.unshift 'lib'
|
8
|
+
require 'beetle'
|
9
|
+
|
10
|
+
namespace :test do
|
11
|
+
namespace :coverage do
|
12
|
+
desc "Delete aggregate coverage data."
|
13
|
+
task(:clean) { rm_f "coverage.data" }
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Aggregate code coverage'
|
17
|
+
task :coverage => "test:coverage:clean"
|
18
|
+
|
19
|
+
Rcov::RcovTask.new(:coverage) do |t|
|
20
|
+
t.libs << "test"
|
21
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
22
|
+
t.output_dir = "test/coverage"
|
23
|
+
t.verbose = true
|
24
|
+
t.rcov_opts << "--exclude '.*' --include-file 'lib/beetle/'"
|
25
|
+
end
|
26
|
+
task :coverage do
|
27
|
+
system 'open test/coverage/index.html'
|
28
|
+
end if RUBY_PLATFORM =~ /darwin/
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
namespace :beetle do
|
33
|
+
task :test do
|
34
|
+
Beetle::Client.new.test
|
35
|
+
end
|
36
|
+
|
37
|
+
task :trace do
|
38
|
+
trap('INT'){ EM.stop_event_loop }
|
39
|
+
Beetle::Client.new.trace
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
namespace :rabbit do
|
44
|
+
def start(node_name, port)
|
45
|
+
script = File.expand_path(File.dirname(__FILE__)+"/script/start_rabbit")
|
46
|
+
puts "starting rabbit #{node_name} on port #{port}"
|
47
|
+
puts "type ^C a RETURN to abort"
|
48
|
+
sleep 1
|
49
|
+
exec "sudo #{script} #{node_name} #{port}"
|
50
|
+
end
|
51
|
+
desc "start rabbit instance 1"
|
52
|
+
task :start1 do
|
53
|
+
start "rabbit1", 5672
|
54
|
+
end
|
55
|
+
desc "start rabbit instance 2"
|
56
|
+
task :start2 do
|
57
|
+
start "rabbit2", 5673
|
58
|
+
end
|
59
|
+
desc "reset rabbit instances (deletes all data!)"
|
60
|
+
task :reset do
|
61
|
+
["rabbit1", "rabbit2"].each do |node|
|
62
|
+
`sudo rabbitmqctl -n #{node} stop_app`
|
63
|
+
`sudo rabbitmqctl -n #{node} reset`
|
64
|
+
`sudo rabbitmqctl -n #{node} start_app`
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
namespace :redis do
|
70
|
+
def config_file(suffix)
|
71
|
+
File.expand_path(File.dirname(__FILE__)+"/etc/redis-#{suffix}.conf")
|
72
|
+
end
|
73
|
+
desc "start main redis"
|
74
|
+
task :start1 do
|
75
|
+
exec "redis-server #{config_file(:master)}"
|
76
|
+
end
|
77
|
+
desc "start slave redis"
|
78
|
+
task :start2 do
|
79
|
+
exec "redis-server #{config_file(:slave)}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
Cucumber::Rake::Task.new(:cucumber) do |t|
|
84
|
+
t.cucumber_opts = "features --format progress"
|
85
|
+
end
|
86
|
+
|
87
|
+
task :default do
|
88
|
+
Rake::Task[:test].invoke
|
89
|
+
Rake::Task[:cucumber].invoke
|
90
|
+
end
|
91
|
+
|
92
|
+
Rake::TestTask.new do |t|
|
93
|
+
t.libs << "test"
|
94
|
+
t.test_files = FileList['test/**/*_test.rb']
|
95
|
+
t.verbose = true
|
96
|
+
end
|
97
|
+
|
98
|
+
require 'rake/rdoctask'
|
99
|
+
|
100
|
+
Rake::RDocTask.new do |rdoc|
|
101
|
+
rdoc.rdoc_dir = 'site/rdoc'
|
102
|
+
rdoc.title = 'Beetle'
|
103
|
+
rdoc.main = 'README.rdoc'
|
104
|
+
rdoc.options << '--line-numbers' << '--inline-source' << '--quiet'
|
105
|
+
rdoc.rdoc_files.include('**/*.rdoc')
|
106
|
+
rdoc.rdoc_files.include('MIT-LICENSE')
|
107
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
108
|
+
end
|
109
|
+
|
110
|
+
desc "build the beetle gem"
|
111
|
+
task :build do
|
112
|
+
system("gem build beetle.gemspec")
|
113
|
+
end
|
data/beetle.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "beetle"
|
3
|
-
s.version = "0.2.
|
3
|
+
s.version = "0.2.5"
|
4
4
|
|
5
5
|
s.required_rubygems_version = ">= 1.3.1"
|
6
6
|
s.authors = ["Stefan Kaes", "Pascal Friederich", "Ali Jelveh", "Sebastian Roebke"]
|
@@ -10,8 +10,8 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.summary = "High Availability AMQP Messaging with Redundant Queues"
|
11
11
|
s.email = "developers@xing.com"
|
12
12
|
s.executables = ["beetle"]
|
13
|
-
s.extra_rdoc_files = [
|
14
|
-
s.files = Dir['{examples,ext,lib}/**/*.rb'] + %w(beetle.gemspec
|
13
|
+
s.extra_rdoc_files = Dir['**/*.rdoc'] + %w(MIT-LICENSE)
|
14
|
+
s.files = Dir['{examples,ext,lib}/**/*.rb'] + Dir['{features,script}/**/*'] + %w(beetle.gemspec Rakefile)
|
15
15
|
s.extensions = 'ext/mkrf_conf.rb'
|
16
16
|
s.homepage = "http://xing.github.com/beetle/"
|
17
17
|
s.rdoc_options = ["--charset=UTF-8"]
|
@@ -0,0 +1,23 @@
|
|
1
|
+
=== Cucumber
|
2
|
+
|
3
|
+
Beetle ships with a cucumber feature to test the automatic redis failover
|
4
|
+
as an integration test.
|
5
|
+
|
6
|
+
To run it, you have to start a RabbitMQ.
|
7
|
+
|
8
|
+
The top level Rakefile comes with targets to start several RabbitMQ instances locally.
|
9
|
+
Make sure the corresponding binaries are in your search path. Open a new shell
|
10
|
+
and execute the following command:
|
11
|
+
|
12
|
+
rake rabbit:start1
|
13
|
+
|
14
|
+
Then you can run the cucumber feature by running:
|
15
|
+
|
16
|
+
cucumber
|
17
|
+
|
18
|
+
or
|
19
|
+
|
20
|
+
rake cucumber
|
21
|
+
|
22
|
+
|
23
|
+
Note: Cucumber will automatically run after the unit test when you run rake.
|
@@ -0,0 +1,105 @@
|
|
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 of "rc-client-1" should be "redis-2"
|
19
|
+
And the redis master of "rc-client-2" should be "redis-2"
|
20
|
+
And the redis master of the beetle handler should be "redis-2"
|
21
|
+
And a system notification for switching from "redis-1" to "redis-2" should be sent
|
22
|
+
Given a redis server "redis-1" exists as master
|
23
|
+
Then the role of redis server "redis-1" should be "slave"
|
24
|
+
|
25
|
+
Scenario: Redis master only temporarily down (no switch necessary)
|
26
|
+
Given a redis configuration server using redis servers "redis-1,redis-2" with clients "rc-client-1,rc-client-2" exists
|
27
|
+
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
28
|
+
And a redis configuration client "rc-client-2" 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 server "redis-1" is down for less seconds than the retry timeout for the redis master check
|
31
|
+
And the retry timeout for the redis master check is reached
|
32
|
+
Then the role of redis server "redis-1" should be "master"
|
33
|
+
Then the role of redis server "redis-2" should be "slave"
|
34
|
+
And the redis master of "rc-client-1" should be "redis-1"
|
35
|
+
And the redis master of "rc-client-2" should be "redis-1"
|
36
|
+
And the redis master of the beetle handler should be "redis-1"
|
37
|
+
|
38
|
+
Scenario: Not all redis configuration clients available (no switch possible)
|
39
|
+
Given a redis configuration server using redis servers "redis-1,redis-2" with clients "rc-client-1,rc-client-2" exists
|
40
|
+
And redis server "redis-1" is down
|
41
|
+
And the retry timeout for the redis master check is reached
|
42
|
+
Then the role of redis server "redis-2" should be "slave"
|
43
|
+
|
44
|
+
Scenario: No redis slave available to become new master (no switch possible)
|
45
|
+
Given a redis configuration server using redis servers "redis-1,redis-2" with clients "rc-client-1,rc-client-2" exists
|
46
|
+
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
47
|
+
And a redis configuration client "rc-client-2" using redis servers "redis-1,redis-2" exists
|
48
|
+
And redis server "redis-1" is down
|
49
|
+
And redis server "redis-2" is down
|
50
|
+
And the retry timeout for the redis master check is reached
|
51
|
+
Then 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 a system notification for no slave available to become new master should be sent
|
54
|
+
|
55
|
+
Scenario: Redis configuration client starts while no redis master available
|
56
|
+
Given redis server "redis-1" is down
|
57
|
+
And redis server "redis-2" is down
|
58
|
+
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
59
|
+
And the retry timeout for the redis master determination is reached
|
60
|
+
Then the redis master of "rc-client-1" should be undefined
|
61
|
+
|
62
|
+
Scenario: Redis configuration client starts while no redis master available but master file exists
|
63
|
+
Given redis server "redis-1" is down
|
64
|
+
And redis server "redis-2" is down
|
65
|
+
And an old redis master file for "rc-client-1" with master "redis-1" exists
|
66
|
+
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
67
|
+
And the retry timeout for the redis master determination is reached
|
68
|
+
Then the redis master of "rc-client-1" should be undefined
|
69
|
+
|
70
|
+
Scenario: Redis configuration client starts while both redis servers are master
|
71
|
+
Given redis server "redis-2" is master
|
72
|
+
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
73
|
+
Then the redis master of "rc-client-1" should be undefined
|
74
|
+
|
75
|
+
Scenario: Redis configuration client starts while both redis servers are master but master file exists
|
76
|
+
Given redis server "redis-2" is master
|
77
|
+
And an old redis master file for "rc-client-1" with master "redis-1" exists
|
78
|
+
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
79
|
+
Then the redis master of "rc-client-1" should be "redis-1"
|
80
|
+
|
81
|
+
Scenario: Redis configuration client starts while both redis servers are slave
|
82
|
+
Given a redis server "redis-3" exists as master
|
83
|
+
And redis server "redis-1" is slave of "redis-3"
|
84
|
+
And redis server "redis-2" is slave of "redis-3"
|
85
|
+
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
86
|
+
Then the redis master of "rc-client-1" should be undefined
|
87
|
+
|
88
|
+
Scenario: Redis configuration client starts while both redis servers are slave but master file exists
|
89
|
+
Given a redis server "redis-3" exists as master
|
90
|
+
And redis server "redis-1" is slave of "redis-3"
|
91
|
+
And redis server "redis-2" is slave of "redis-3"
|
92
|
+
And an old redis master file for "rc-client-1" with master "redis-1" exists
|
93
|
+
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
94
|
+
Then the redis master of "rc-client-1" should be undefined
|
95
|
+
|
96
|
+
Scenario: Redis configuration client starts while there is a redis master but no slave
|
97
|
+
Given redis server "redis-2" is down
|
98
|
+
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
99
|
+
Then the redis master of "rc-client-1" should be undefined
|
100
|
+
|
101
|
+
Scenario: Redis configuration client starts while there is a redis master but no slave but master file exists
|
102
|
+
Given redis server "redis-2" is down
|
103
|
+
And an old redis master file for "rc-client-1" with master "redis-1" exists
|
104
|
+
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
105
|
+
Then the redis master of "rc-client-1" should be "redis-1"
|