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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a35a259d101d2b0623ae59f12e901b0872308a372ecaaecd43d95de13e38bec
4
- data.tar.gz: bdc124f3b915978f1bee1265e9e331f3f48a908a5c9c5ef9792c8779037ca33a
3
+ metadata.gz: c8a5f1b489f657fd5e286f6461b13ee314b6b4abe3dae6452bb53ca81ce23255
4
+ data.tar.gz: 183a0d96adf42fe4648f946203527edd1b065b7c961b62b825e6f5c6483edf7e
5
5
  SHA512:
6
- metadata.gz: 6b0b8d21c4b678de78c06b887075eb175d1ddcd902c8f40dc683fd5d2a3c160422e7ee94842db7f4620e1ffccf7ffa7051e74afe7f81d9d8206f206022034fff
7
- data.tar.gz: 295f9b96e41cbde54221107736cf3bfaec30dade8e728ceb0dc0f7ea4a91bf0e0a5fdbcf6c3868a6ae54dc5964901451c2ec08a756c3e59582cf455eaa6f938b
6
+ metadata.gz: 3eaa2e3b642b8c3dd5d5062a4327d5499e87c3c02d27493b33f9f42fa1bb2964e2f6b5af386c88525b2343e075ba61e42fe9a75ce459d2a98ef949e418ffe7e1
7
+ data.tar.gz: cf80066e492795627ef460eec5c05a222a5af569d87e62084ec865939e715a6f4c6ca902f618d9039f7d650c9208b694a30e28dd81bd23571d71b1a1ff7ea287
@@ -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-2015 {XING AG}[http://www.xing.com/]
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.
@@ -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
 
@@ -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 "uuid4r", ">= 0.1.2"
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"
@@ -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(",").map { |redis_name| TestDaemons::Redis[redis_name].ip_with_port }.join(",")
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
- redis_servers = redis_names.split(",").map do |redis_name|
31
- TestDaemons::Redis[redis_name].ip_with_port
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 = nil
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
- master = true and break if TestDaemons::Redis[redis_name].ip_with_port == server_info
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| # " for emacs :(
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
- assert_equal TestDaemons::Redis[redis_name].ip_with_port, file_contents
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
- assert_equal TestDaemons::Redis[redis_name].ip_with_port, client.rpc(:echo, 'nil').second
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) {|message| client.deduplication_store.redis.server rescue "no redis master"}
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
@@ -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 '/initiate_master_switch', ''
91
+ response = http.post "/initiate_master_switch?system_name=#{system_name}", ""
92
92
  response
93
93
  end
94
94
 
@@ -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'
@@ -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
- return unless @config.dead_lettering_enabled?
14
-
15
- dead_letter_queue_name = dead_letter_queue_name(target_queue)
16
-
17
- logger.debug("Beetle: creating dead letter queue #{dead_letter_queue_name} with opts: #{creation_keys.inspect}")
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
- logger.debug("Beetle: setting #{target_queue} as dead letter queue of #{dead_letter_queue_name} on all servers")
24
- set_dead_letter_policies!(
25
- servers,
26
- dead_letter_queue_name,
27
- :message_ttl => @config.dead_lettering_msg_ttl,
28
- :routing_key => target_queue
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 set_dead_letter_policies!(servers, queue_name, options={})
33
- servers.each { |server| set_dead_letter_policy!(server, queue_name, options) }
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 set_dead_letter_policy!(server, queue_name, options={})
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
- if response.code != "204"
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 file
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
@@ -146,8 +146,16 @@ module Beetle
146
146
  end
147
147
 
148
148
  # generate uuid for publishing
149
- def self.generate_uuid
150
- UUID4R::uuid(4)
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
@@ -159,15 +159,16 @@ module Beetle
159
159
 
160
160
  def new_bunny
161
161
  b = Bunny.new(
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,
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
- Beetle::Timer.timeout(1) do
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
@@ -1,3 +1,3 @@
1
1
  module Beetle
2
- VERSION = "2.2.3"
2
+ VERSION = "2.3.0"
3
3
  end
@@ -10,16 +10,16 @@ OptionParser.new do |opt|
10
10
  end
11
11
 
12
12
  libs = " -r irb/completion"
13
- libs << %( -r ubygems)
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 'ruby-debug'
19
- libs << " -r ruby-debug"
18
+ require 'byebug'
19
+ libs << " -r byebug"
20
20
  puts "=> Debugger enabled"
21
21
  rescue Exception
22
- puts "You need to install ruby-debug to run the console in debugging mode. With gems, use 'gem install ruby-debug'"
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
- @dead_lettering = DeadLettering.new(Configuration.new)
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(:set_dead_letter_policy!).
14
+ @dead_lettering.expects(:set_queue_policy!).
13
15
  with("a", "QUEUE_NAME", :message_ttl => 10000)
14
- @dead_lettering.expects(:set_dead_letter_policy!).
16
+ @dead_lettering.expects(:set_queue_policy!).
15
17
  with("b", "QUEUE_NAME", :message_ttl => 10000)
16
18
 
17
- @dead_lettering.set_dead_letter_policies!(servers, "QUEUE_NAME", :message_ttl => 10000)
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.set_dead_letter_policy!(@server, "")
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.set_dead_letter_policy!("", @queue_name)
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.set_dead_letter_policy!(@server, @queue_name)
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.set_dead_letter_policy!(@server, @queue_name)
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.set_dead_letter_policy!(@server, @queue_name, :message_ttl => 10000)
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.set_dead_letter_policy!(@server, @queue_name)
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 turned off by default" do
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(:set_dead_letter_policies!).never
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(:set_dead_letter_policies!).with(@servers, @queue_name)
126
- @dead_lettering.expects(:set_dead_letter_policies!).with(@servers, "#{@queue_name}_dead_letter",
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__)
@@ -24,6 +24,7 @@ module Beetle
24
24
  :pass => "guest",
25
25
  :vhost => "/",
26
26
  :socket_timeout => 0,
27
+ :connect_timeout => 5,
27
28
  :frame_max => 131072,
28
29
  :channel_max => 2047,
29
30
  :spec => '09'
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.2.3
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-18 00:00:00.000000000 Z
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.10
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.10
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