beetle 0.3.0.rc.8 → 0.3.0.rc.9
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/RELEASE_NOTES.rdoc +7 -1
- data/beetle.gemspec +3 -3
- data/examples/test_publisher.rb +32 -0
- data/features/redis_auto_failover.feature +39 -0
- data/features/step_definitions/redis_auto_failover_steps.rb +13 -3
- data/features/support/test_daemons/redis_configuration_server.rb +11 -5
- data/lib/beetle/redis_configuration_http_server.rb +32 -1
- data/lib/beetle/redis_configuration_server.rb +12 -0
- data/lib/beetle/version.rb +1 -1
- data/test/beetle/redis_configuration_server_test.rb +22 -0
- metadata +14 -14
- data/script/console~ +0 -2
data/RELEASE_NOTES.rdoc
CHANGED
@@ -1,9 +1,15 @@
|
|
1
1
|
= Release Notes
|
2
2
|
|
3
|
-
== Version 0.3.0
|
3
|
+
== Version 0.3.0
|
4
4
|
|
5
|
+
* allow accelerating master switch via POST to redis configuration server
|
6
|
+
* embedded http server into the redis configuration server (port 8080)
|
7
|
+
* fixed a problem with redis shutdown command
|
8
|
+
* upgraded to redis 2.2.2
|
9
|
+
* upgraded to amqp gem version 0.8 line
|
5
10
|
* use hiredis as the redis backend, which overcomes lack of proper time-outs in the "generic" redis-rb
|
6
11
|
gem for Ruby 1.9
|
12
|
+
* use fully qualified hostnames to identify redis configuration clients
|
7
13
|
|
8
14
|
== Version 0.2.9.8
|
9
15
|
|
data/beetle.gemspec
CHANGED
@@ -36,9 +36,9 @@ Gem::Specification.new do |s|
|
|
36
36
|
s.add_runtime_dependency("bunny", ["= 0.7.8"])
|
37
37
|
s.add_runtime_dependency("redis", ["= 2.2.2"])
|
38
38
|
s.add_runtime_dependency("hiredis", ["= 0.3.2"])
|
39
|
-
s.add_runtime_dependency("amq-client", ["= 0.8.
|
40
|
-
s.add_runtime_dependency("amq-protocol", ["= 0.8.
|
41
|
-
s.add_runtime_dependency("amqp", ["= 0.8.
|
39
|
+
s.add_runtime_dependency("amq-client", ["= 0.8.5"])
|
40
|
+
s.add_runtime_dependency("amq-protocol", ["= 0.8.3"])
|
41
|
+
s.add_runtime_dependency("amqp", ["= 0.8.2"])
|
42
42
|
s.add_runtime_dependency("activesupport", [">= 2.3.4"])
|
43
43
|
s.add_runtime_dependency("eventmachine_httpserver", [">= 0.2.1"])
|
44
44
|
s.add_runtime_dependency("daemons", [">= 1.0.10"])
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# attempts.rb
|
2
|
+
# this example shows you how to use the exception limiting feature of beetle
|
3
|
+
# it allows you to control the number of retries your handler will go through
|
4
|
+
# with one message before giving up on it
|
5
|
+
#
|
6
|
+
# ! check the examples/README.rdoc for information on starting your redis/rabbit !
|
7
|
+
#
|
8
|
+
# start it with ruby attempts.rb
|
9
|
+
|
10
|
+
require "rubygems"
|
11
|
+
require File.expand_path("../lib/beetle", File.dirname(__FILE__))
|
12
|
+
require "eventmachine"
|
13
|
+
|
14
|
+
# set Beetle log level to info, less noisy than debug
|
15
|
+
Beetle.config.logger.level = Logger::INFO
|
16
|
+
|
17
|
+
# setup client
|
18
|
+
client = Beetle::Client.new
|
19
|
+
client.register_message(:test)
|
20
|
+
|
21
|
+
n = 0
|
22
|
+
EM.run do
|
23
|
+
EM.add_periodic_timer(0.1) do
|
24
|
+
data = (n+=1)
|
25
|
+
client.logger.info "publishing #{data}"
|
26
|
+
client.publish(:test, data)
|
27
|
+
end
|
28
|
+
trap("INT") do
|
29
|
+
client.stop_publishing
|
30
|
+
EM.stop_event_loop
|
31
|
+
end
|
32
|
+
end
|
@@ -107,3 +107,42 @@ Feature: Redis auto failover
|
|
107
107
|
Scenario: Redis configuation server should embed a http server
|
108
108
|
Given a redis configuration server using redis servers "redis-1,redis-2" with clients "rc-client-1,rc-client-2" exists
|
109
109
|
Then the redis configuration server should answer http requests
|
110
|
+
|
111
|
+
Scenario: Accelerated redis master switch when master is down
|
112
|
+
Given a redis configuration server using redis servers "redis-1,redis-2" with clients "rc-client-1,rc-client-2" exists
|
113
|
+
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
114
|
+
And a redis configuration client "rc-client-2" using redis servers "redis-1,redis-2" exists
|
115
|
+
And a beetle handler using the redis-master file from "rc-client-1" exists
|
116
|
+
And redis server "redis-1" is down
|
117
|
+
And an immediate master switch is initiated and responds with 201
|
118
|
+
Then a system notification for "redis-1" not being available should be sent
|
119
|
+
And the role of redis server "redis-2" should be "master"
|
120
|
+
And the redis master of "rc-client-1" should be "redis-2"
|
121
|
+
And the redis master of "rc-client-2" should be "redis-2"
|
122
|
+
And the redis master of the beetle handler should be "redis-2"
|
123
|
+
And a system notification for switching from "redis-1" to "redis-2" should be sent
|
124
|
+
Given a redis server "redis-1" exists as master
|
125
|
+
Then the role of redis server "redis-1" should be "slave"
|
126
|
+
|
127
|
+
Scenario: Accelerated redis master switch when master is up
|
128
|
+
Given a redis configuration server using redis servers "redis-1,redis-2" with clients "rc-client-1,rc-client-2" exists
|
129
|
+
And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
130
|
+
And a redis configuration client "rc-client-2" using redis servers "redis-1,redis-2" exists
|
131
|
+
And a beetle handler using the redis-master file from "rc-client-1" exists
|
132
|
+
And an immediate master switch is initiated and responds with 200
|
133
|
+
Then the role of redis server "redis-1" should be "master"
|
134
|
+
And the redis master of "rc-client-1" should be "redis-1"
|
135
|
+
And the redis master of "rc-client-2" should be "redis-1"
|
136
|
+
And the redis master of the beetle handler should be "redis-1"
|
137
|
+
And the role of redis server "redis-2" should be "slave"
|
138
|
+
|
139
|
+
# Scenario: Running the system for a few seconds to perform manual testing
|
140
|
+
# Given a redis configuration server using redis servers "redis-1,redis-2" with clients "rc-client-1,rc-client-2" exists
|
141
|
+
# And a redis configuration client "rc-client-1" using redis servers "redis-1,redis-2" exists
|
142
|
+
# And a redis configuration client "rc-client-2" using redis servers "redis-1,redis-2" exists
|
143
|
+
# And a beetle handler using the redis-master file from "rc-client-1" exists
|
144
|
+
# And the redis master of "rc-client-1" should be "redis-1"
|
145
|
+
# And the redis master of "rc-client-2" should be "redis-1"
|
146
|
+
# And the redis master of the beetle handler should be "redis-1"
|
147
|
+
# And the role of redis server "redis-2" should be "slave"
|
148
|
+
# Then the system can run for a while without dying
|
@@ -134,7 +134,17 @@ Then /^a system notification for no slave available to become new master should
|
|
134
134
|
end
|
135
135
|
|
136
136
|
Then /^the redis configuration server should answer http requests$/ do
|
137
|
-
TestDaemons::RedisConfigurationServer.answers_text_requests?
|
138
|
-
TestDaemons::RedisConfigurationServer.answers_html_requests?
|
139
|
-
TestDaemons::RedisConfigurationServer.answers_json_requests?
|
137
|
+
assert TestDaemons::RedisConfigurationServer.answers_text_requests?
|
138
|
+
assert TestDaemons::RedisConfigurationServer.answers_html_requests?
|
139
|
+
assert TestDaemons::RedisConfigurationServer.answers_json_requests?
|
140
|
+
end
|
141
|
+
|
142
|
+
Given /^an immediate master switch is initiated and responds with (\d+)$/ do |response_code|
|
143
|
+
response = TestDaemons::RedisConfigurationServer.initiate_master_switch
|
144
|
+
assert_equal response_code, response.code
|
145
|
+
sleep 1
|
146
|
+
end
|
147
|
+
|
148
|
+
Then /^the system can run for a while without dying$/ do
|
149
|
+
sleep 60
|
140
150
|
end
|
@@ -66,9 +66,10 @@ module TestDaemons
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def self.answers_html_requests?
|
69
|
-
|
70
|
-
|
71
|
-
|
69
|
+
response1 = get_status("/", "text/html")
|
70
|
+
response2 = get_status("/.html", "text/html")
|
71
|
+
response1.code == '200' && response2.code == '200' &&
|
72
|
+
response1.content_type == "text/html" && response2.content_type == "text/html"
|
72
73
|
rescue
|
73
74
|
false
|
74
75
|
end
|
@@ -79,9 +80,14 @@ module TestDaemons
|
|
79
80
|
request = Net::HTTP::Get.new(uri.request_uri)
|
80
81
|
request['Accept'] = content_type
|
81
82
|
response = http.request(request)
|
82
|
-
# $stderr.puts response.content_type
|
83
|
-
# $stderr.puts response.body
|
84
83
|
response
|
85
84
|
end
|
85
|
+
|
86
|
+
def self.initiate_master_switch
|
87
|
+
http = Net::HTTP.new('127.0.0.1', 8080)
|
88
|
+
response = http.post '/initiate_master_switch', ''
|
89
|
+
response
|
90
|
+
end
|
91
|
+
|
86
92
|
end
|
87
93
|
end
|
@@ -24,6 +24,7 @@ module Beetle
|
|
24
24
|
# @http_post_content
|
25
25
|
# @http_headers
|
26
26
|
response = EM::DelegatedHttpResponse.new(self)
|
27
|
+
response.headers['Refresh'] = '3; url=/'
|
27
28
|
# headers = @http_headers.split("\0").inject({}){|h, s| (s =~ /^([^:]+): (.*)$/ && (h[$1] = $2)); h }
|
28
29
|
|
29
30
|
case @http_request_uri
|
@@ -36,6 +37,8 @@ module Beetle
|
|
36
37
|
when "/.txt"
|
37
38
|
response.content_type 'text/plain'
|
38
39
|
server_status(response, "plain")
|
40
|
+
when '/initiate_master_switch'
|
41
|
+
initiate_master_switch(response)
|
39
42
|
else
|
40
43
|
not_found(response)
|
41
44
|
end
|
@@ -74,7 +77,13 @@ module Beetle
|
|
74
77
|
row =~/(^[^:]+): (.*)$/
|
75
78
|
b << "<tr><td>#{$1}</td><td>#{$2}</td></tr>\n"
|
76
79
|
end
|
77
|
-
b << "</table
|
80
|
+
b << "</table>"
|
81
|
+
unless status[:redis_master_available?]
|
82
|
+
b << "<form name='masterswitch' method='post' action='/initiate_master_switch'>"
|
83
|
+
b << "<a href='javascript: document.masterswitch.submit();'>Initiate master switch</a>"
|
84
|
+
b << "</form>"
|
85
|
+
end
|
86
|
+
b << "</body></html>"
|
78
87
|
end
|
79
88
|
|
80
89
|
def html_styles(status)
|
@@ -86,10 +95,32 @@ body { margin: 1em; }
|
|
86
95
|
table tr:nth-child(2n+1){ background:#fff; }
|
87
96
|
td { padding: 0.1em 0.2em; }
|
88
97
|
h1 { color: #{warn_color}; margin-bottom: 0.2em;}
|
98
|
+
a:link, a:visited {text-decoration:none; color:#A52A2A;}
|
99
|
+
a:hover, a:active {text-decoration:none; color:#FF0000;}
|
100
|
+
a {
|
101
|
+
font-size: 2em; padding: 10px; background: #cdcdcd;
|
102
|
+
-moz-border-radius: 5px;
|
103
|
+
border-radius: 5px;
|
104
|
+
-moz-box-shadow: 2px 2px 2px #bbb;
|
105
|
+
-webkit-box-shadow: 2px 2px 2px #bbb;
|
106
|
+
box-shadow: 2px 2px 2px #bbb;
|
107
|
+
}
|
108
|
+
form { margin-top: 1em; }
|
89
109
|
</style>
|
90
110
|
EOS
|
91
111
|
end
|
92
112
|
|
113
|
+
def initiate_master_switch(response)
|
114
|
+
response.content_type 'text/plain'
|
115
|
+
if config_server.initiate_master_switch
|
116
|
+
response.status = 201
|
117
|
+
response.content = "Master switch initiated"
|
118
|
+
else
|
119
|
+
response.status = 200
|
120
|
+
response.content = "No master switch necessary"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
93
124
|
def not_found(response)
|
94
125
|
response.content_type 'text/plain'
|
95
126
|
response.status = 404
|
@@ -56,6 +56,7 @@ module Beetle
|
|
56
56
|
:redis_master => current_master.try(:server).to_s,
|
57
57
|
:redis_master_available? => master_available?,
|
58
58
|
:redis_slaves_available => available_slaves.map(&:server),
|
59
|
+
:switch_in_progress => paused?,
|
59
60
|
}
|
60
61
|
end
|
61
62
|
|
@@ -147,6 +148,17 @@ module Beetle
|
|
147
148
|
redis.slaves
|
148
149
|
end
|
149
150
|
|
151
|
+
# initiate a master switch if the current master is not available and no switch is in progress
|
152
|
+
def initiate_master_switch
|
153
|
+
redis.refresh
|
154
|
+
available, switch_in_progress = master_available?, paused?
|
155
|
+
logger.debug "initiating master switch: already in progress = #{switch_in_progress}"
|
156
|
+
unless available || switch_in_progress
|
157
|
+
master_unavailable!
|
158
|
+
end
|
159
|
+
!available || switch_in_progress
|
160
|
+
end
|
161
|
+
|
150
162
|
private
|
151
163
|
|
152
164
|
def check_redis_configuration
|
data/lib/beetle/version.rb
CHANGED
@@ -63,6 +63,28 @@ module Beetle
|
|
63
63
|
test "should be able to report current status" do
|
64
64
|
assert @server.status.is_a?(Hash)
|
65
65
|
end
|
66
|
+
|
67
|
+
test "should not execute a conditional master switch if the current master is available" do
|
68
|
+
@server.expects(:master_available?).returns(true)
|
69
|
+
@server.expects(:paused?).returns(false)
|
70
|
+
@server.expects(:master_unavailable!).never
|
71
|
+
assert !@server.initiate_master_switch
|
72
|
+
end
|
73
|
+
|
74
|
+
test "should not execute a conditional master switch if a switch is already in progress" do
|
75
|
+
@server.expects(:master_available?).returns(false)
|
76
|
+
@server.expects(:paused?).returns(true)
|
77
|
+
@server.expects(:master_unavailable!).never
|
78
|
+
assert @server.initiate_master_switch
|
79
|
+
end
|
80
|
+
|
81
|
+
test "should execute a conditional master switch if the current master is unavailable and no switch is in progress yet" do
|
82
|
+
@server.expects(:master_available?).returns(false)
|
83
|
+
@server.expects(:master_unavailable!).once
|
84
|
+
@server.expects(:paused?).returns(false)
|
85
|
+
assert @server.initiate_master_switch
|
86
|
+
end
|
87
|
+
|
66
88
|
end
|
67
89
|
|
68
90
|
class RedisConfigurationServerInvalidationTest < Test::Unit::TestCase
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: beetle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 15424055
|
5
5
|
prerelease: 6
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 3
|
9
9
|
- 0
|
10
10
|
- rc
|
11
|
-
-
|
12
|
-
version: 0.3.0.rc.
|
11
|
+
- 9
|
12
|
+
version: 0.3.0.rc.9
|
13
13
|
platform: ruby
|
14
14
|
authors:
|
15
15
|
- Stefan Kaes
|
@@ -21,7 +21,7 @@ autorequire:
|
|
21
21
|
bindir: bin
|
22
22
|
cert_chain: []
|
23
23
|
|
24
|
-
date: 2011-10-
|
24
|
+
date: 2011-10-31 00:00:00 +01:00
|
25
25
|
default_executable: beetle
|
26
26
|
dependencies:
|
27
27
|
- !ruby/object:Gem::Dependency
|
@@ -96,12 +96,12 @@ dependencies:
|
|
96
96
|
requirements:
|
97
97
|
- - "="
|
98
98
|
- !ruby/object:Gem::Version
|
99
|
-
hash:
|
99
|
+
hash: 53
|
100
100
|
segments:
|
101
101
|
- 0
|
102
102
|
- 8
|
103
|
-
-
|
104
|
-
version: 0.8.
|
103
|
+
- 5
|
104
|
+
version: 0.8.5
|
105
105
|
type: :runtime
|
106
106
|
version_requirements: *id005
|
107
107
|
- !ruby/object:Gem::Dependency
|
@@ -112,12 +112,12 @@ dependencies:
|
|
112
112
|
requirements:
|
113
113
|
- - "="
|
114
114
|
- !ruby/object:Gem::Version
|
115
|
-
hash:
|
115
|
+
hash: 57
|
116
116
|
segments:
|
117
117
|
- 0
|
118
118
|
- 8
|
119
|
-
-
|
120
|
-
version: 0.8.
|
119
|
+
- 3
|
120
|
+
version: 0.8.3
|
121
121
|
type: :runtime
|
122
122
|
version_requirements: *id006
|
123
123
|
- !ruby/object:Gem::Dependency
|
@@ -128,12 +128,12 @@ dependencies:
|
|
128
128
|
requirements:
|
129
129
|
- - "="
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
hash:
|
131
|
+
hash: 59
|
132
132
|
segments:
|
133
133
|
- 0
|
134
134
|
- 8
|
135
|
-
-
|
136
|
-
version: 0.8.
|
135
|
+
- 2
|
136
|
+
version: 0.8.2
|
137
137
|
type: :runtime
|
138
138
|
version_requirements: *id007
|
139
139
|
- !ruby/object:Gem::Dependency
|
@@ -282,6 +282,7 @@ files:
|
|
282
282
|
- examples/redundant.rb
|
283
283
|
- examples/rpc.rb
|
284
284
|
- examples/simple.rb
|
285
|
+
- examples/test_publisher.rb
|
285
286
|
- lib/beetle/base.rb
|
286
287
|
- lib/beetle/client.rb
|
287
288
|
- lib/beetle/commands/configuration_client.rb
|
@@ -314,7 +315,6 @@ files:
|
|
314
315
|
- features/support/test_daemons/redis_configuration_client.rb
|
315
316
|
- features/support/test_daemons/redis_configuration_server.rb
|
316
317
|
- script/console
|
317
|
-
- script/console~
|
318
318
|
- script/start_rabbit
|
319
319
|
- beetle.gemspec
|
320
320
|
- Rakefile
|
data/script/console~
DELETED