beetle 0.3.0.rc.8 → 0.3.0.rc.9
Sign up to get free protection for your applications and to get access to all the features.
- 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