beetle 0.3.0.rc.18 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -34,11 +34,11 @@ Gem::Specification.new do |s|
34
34
  s.specification_version = 3
35
35
  s.add_runtime_dependency("uuid4r", [">= 0.1.2"])
36
36
  s.add_runtime_dependency("bunny", ["= 0.7.9"])
37
- s.add_runtime_dependency("redis", ["= 3.0.1"])
37
+ s.add_runtime_dependency("redis", [">= 2.2.2", "< 3.1"])
38
38
  s.add_runtime_dependency("hiredis", ["= 0.4.5"])
39
- s.add_runtime_dependency("amq-client", ["= 0.9.10"])
40
- s.add_runtime_dependency("amq-protocol", ["= 1.0.1"])
41
- s.add_runtime_dependency("amqp", ["= 0.9.8"])
39
+ s.add_runtime_dependency("amq-client", ["= 0.9.12"])
40
+ s.add_runtime_dependency("amq-protocol", ["= 1.2.0"])
41
+ s.add_runtime_dependency("amqp", ["= 0.9.10"])
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"])
@@ -74,8 +74,10 @@ module TestDaemons
74
74
  false
75
75
  end
76
76
 
77
+ HTTP_SERVER_PORT = RUBY_PLATFORM =~ /darwin/ ? 9080 : 8080
78
+
77
79
  def self.get_status(path, content_type)
78
- uri = URI.parse("http://127.0.0.1:8080#{path}")
80
+ uri = URI.parse("http://127.0.0.1:#{HTTP_SERVER_PORT}#{path}")
79
81
  http = Net::HTTP.new(uri.host, uri.port)
80
82
  request = Net::HTTP::Get.new(uri.request_uri)
81
83
  request['Accept'] = content_type
@@ -84,7 +86,7 @@ module TestDaemons
84
86
  end
85
87
 
86
88
  def self.initiate_master_switch
87
- http = Net::HTTP.new('127.0.0.1', 8080)
89
+ http = Net::HTTP.new('127.0.0.1', HTTP_SERVER_PORT)
88
90
  response = http.post '/initiate_master_switch', ''
89
91
  response
90
92
  end
@@ -79,9 +79,10 @@ module Beetle
79
79
  Daemons.run_proc("redis_configuration_server", :log_output => true, :dir_mode => dir_mode, :dir => dir) do
80
80
  config_server = Beetle::RedisConfigurationServer.new
81
81
  Beetle::RedisConfigurationHttpServer.config_server = config_server
82
+ http_server_port = RUBY_PLATFORM =~ /darwin/ ? 9080 : 8080
82
83
  EM.run do
83
84
  config_server.start
84
- EM.start_server '0.0.0.0', 8080, Beetle::RedisConfigurationHttpServer
85
+ EM.start_server '0.0.0.0', http_server_port, Beetle::RedisConfigurationHttpServer
85
86
  end
86
87
  end
87
88
  end
@@ -46,4 +46,36 @@ class Redis #:nodoc:
46
46
  info["role"] == "slave" && info["master_host"] == host && info["master_port"] == port.to_s
47
47
  end
48
48
 
49
+ # compatibility layer for redis 2.2.2
50
+ # remove this once all our apps have upgraded to 3.x
51
+ if Redis::VERSION < "3.0"
52
+
53
+ # Redis 2 tries to establish a connection on inspect. this is evil!
54
+ def inspect
55
+ super
56
+ end
57
+
58
+ # redis 2.2.2 shutdown implementation does not disconnect from the redis server.
59
+ # this leaves the connection in an inconsistent state and causes the next command to silently fail.
60
+ # this in turn breaks our cucumber test scenarios.
61
+ # fix this here, until a new version is released which fixes the problem.
62
+
63
+ alias_method :broken_shutdown, :shutdown
64
+
65
+ # Synchronously save the dataset to disk and then shut down the server.
66
+ def shutdown
67
+ synchronize do
68
+ begin
69
+ @client.call [:shutdown]
70
+ rescue Errno::ECONNREFUSED
71
+ ensure
72
+ @client.disconnect
73
+ end
74
+ end
75
+ end
76
+
77
+ def msetnx(*values)
78
+ super != 0
79
+ end
80
+ end
49
81
  end
@@ -7,6 +7,8 @@ module Beetle
7
7
  # create a new subscriber instance
8
8
  def initialize(client, options = {}) #:nodoc:
9
9
  super
10
+ @status = :idle
11
+ @request_stop = false
10
12
  @servers.concat @client.additional_subscription_servers
11
13
  @handlers = {}
12
14
  @connections = {}
@@ -51,9 +53,19 @@ module Beetle
51
53
  if @connections.empty?
52
54
  EM.stop_event_loop
53
55
  else
54
- server, connection = @connections.shift
55
- logger.debug "Beetle: closing connection to #{server}"
56
- connection.close { stop! }
56
+ # Only kill connections if not currently processing a message
57
+ # otherwise messages can get ACKed after the connection is closed
58
+ # resulting in the ACK not being received and hence the
59
+ # message being re-delivered
60
+ if @status == :idle
61
+ server, connection = @connections.shift
62
+ logger.debug "Beetle: closing connection to #{server}"
63
+ connection.close { stop! }
64
+ else
65
+ # else ask for stop. After processing the current message the
66
+ # stop will be re-attempted
67
+ @request_stop = true
68
+ end
57
69
  end
58
70
  end
59
71
 
@@ -125,6 +137,7 @@ module Beetle
125
137
  server = @server
126
138
  lambda do |header, data|
127
139
  begin
140
+ @status = :busy
128
141
  # logger.debug "Beetle: received message"
129
142
  processor = Handler.create(handler, opts)
130
143
  message_options = opts.merge(:server => server, :store => @client.deduplication_store)
@@ -150,6 +163,10 @@ module Beetle
150
163
  ensure
151
164
  # processing_completed swallows all exceptions, so we don't need to protect this call
152
165
  processor.processing_completed
166
+ @status = :idle
167
+ if @request_stop
168
+ stop!
169
+ end
153
170
  end
154
171
  end
155
172
  end
@@ -1,3 +1,3 @@
1
1
  module Beetle
2
- VERSION = "0.3.0.rc.18"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -56,7 +56,7 @@ module Beetle
56
56
  end
57
57
 
58
58
  class HiredisLoadedTest < Test::Unit::TestCase
59
- test 'should be using hiredis instead of the redis ruby backend' do
59
+ test "should be using hiredis instead of the redis ruby backend" do
60
60
  assert defined?(Hiredis)
61
61
  end
62
62
  end
@@ -66,9 +66,27 @@ module Beetle
66
66
  @r = Redis.new(:host => "localhost", :port => 6390)
67
67
  end
68
68
 
69
- test "redis shutdown implementation should call :shutdown and return nil" do
70
- @r.client.expects(:call).with([:shutdown]).once.raises(Redis::ConnectionError)
71
- assert_nil @r.shutdown
69
+ if Redis::VERSION < "3.0"
70
+
71
+ test "orginal redis shutdown implementation is broken" do
72
+ @r.client.expects(:call_without_reply).with([:shutdown]).once
73
+ @r.client.expects(:disconnect).never
74
+ @r.broken_shutdown
75
+ end
76
+
77
+ test "patched redis shutdown implementation should call :shutdown and rescue Errno::ECONNREFUSED" do
78
+ @r.client.expects(:call).with([:shutdown]).once.raises(Errno::ECONNREFUSED)
79
+ @r.client.expects(:disconnect).once
80
+ @r.shutdown
81
+ end
82
+
83
+ else
84
+
85
+ test "redis shutdown implementation should call :shutdown and return nil" do
86
+ @r.client.expects(:call).with([:shutdown]).once.raises(Redis::ConnectionError)
87
+ assert_nil @r.shutdown
88
+ end
89
+
72
90
  end
73
91
  end
74
92
 
@@ -22,7 +22,7 @@ module Beetle
22
22
  assert_equal channel, @sub.send(:channel, "donald:1")
23
23
  end
24
24
 
25
- test "stop! should close all amqp connections and then stop the event loop" do
25
+ test "stop! should close all amqp connections and then stop the event loop if no handler is currently running" do
26
26
  connection1 = mock('con1')
27
27
  connection1.expects(:close).yields
28
28
  connection2 = mock('con2')
@@ -30,6 +30,19 @@ module Beetle
30
30
  @sub.instance_variable_set "@connections", [["server1", connection1], ["server2",connection2]]
31
31
  EM.expects(:stop_event_loop)
32
32
  @sub.send(:stop!)
33
+ assert !@sub.instance_variable_get("@request_stop")
34
+ end
35
+
36
+ test "stop! should not stop the event loop if a handler is currently running" do
37
+ @sub.instance_variable_set "@status", :busy
38
+ connection1 = mock('con1')
39
+ connection1.expects(:close).never
40
+ connection2 = mock('con2')
41
+ connection2.expects(:close).never
42
+ @sub.instance_variable_set "@connections", [["server1", connection1], ["server2",connection2]]
43
+ EM.expects(:stop_event_loop).never
44
+ @sub.send(:stop!)
45
+ assert @sub.instance_variable_get("@request_stop")
33
46
  end
34
47
 
35
48
  end
@@ -224,6 +237,14 @@ module Beetle
224
237
  assert_nothing_raised { @callback.call(header, 'foo') }
225
238
  end
226
239
 
240
+ test "should call stop! if @request_stop has been set" do
241
+ header = header_with_params({})
242
+ Message.any_instance.expects(:process).raises(Exception.new("don't worry"))
243
+ @sub.instance_variable_set("@request_stop", true)
244
+ @sub.expects(:stop!)
245
+ assert_nothing_raised { @callback.call(header, 'foo') }
246
+ end
247
+
227
248
  test "should call reject on the message header when processing the handler returns true on reject?" do
228
249
  header = header_with_params({})
229
250
  result = mock("result")
@@ -3,10 +3,11 @@ if RUBY_VERSION >= "1.9"
3
3
  require 'simplecov'
4
4
  SimpleCov.start do
5
5
  add_filter "/test/"
6
+ add_filter "/lib/beetle/redis_ext.rb"
6
7
  end
7
8
  end
8
9
  require 'test/unit'
9
- require 'mocha'
10
+ require 'mocha/setup'
10
11
  require 'active_support/testing/declarative'
11
12
 
12
13
  require File.expand_path(File.dirname(__FILE__) + '/../lib/beetle')
metadata CHANGED
@@ -1,8 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: beetle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0.rc.18
5
- prerelease: 6
4
+ prerelease:
5
+ version: 0.3.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Stefan Kaes
@@ -13,168 +13,174 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2013-01-12 00:00:00.000000000 Z
16
+ date: 2013-03-12 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: uuid4r
20
20
  requirement: !ruby/object:Gem::Requirement
21
- none: false
22
21
  requirements:
23
22
  - - ! '>='
24
23
  - !ruby/object:Gem::Version
25
24
  version: 0.1.2
25
+ none: false
26
26
  type: :runtime
27
- prerelease: false
28
27
  version_requirements: !ruby/object:Gem::Requirement
29
- none: false
30
28
  requirements:
31
29
  - - ! '>='
32
30
  - !ruby/object:Gem::Version
33
31
  version: 0.1.2
32
+ none: false
33
+ prerelease: false
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: bunny
36
36
  requirement: !ruby/object:Gem::Requirement
37
- none: false
38
37
  requirements:
39
38
  - - '='
40
39
  - !ruby/object:Gem::Version
41
40
  version: 0.7.9
41
+ none: false
42
42
  type: :runtime
43
- prerelease: false
44
43
  version_requirements: !ruby/object:Gem::Requirement
45
- none: false
46
44
  requirements:
47
45
  - - '='
48
46
  - !ruby/object:Gem::Version
49
47
  version: 0.7.9
48
+ none: false
49
+ prerelease: false
50
50
  - !ruby/object:Gem::Dependency
51
51
  name: redis
52
52
  requirement: !ruby/object:Gem::Requirement
53
- none: false
54
53
  requirements:
55
- - - '='
54
+ - - ! '>='
56
55
  - !ruby/object:Gem::Version
57
- version: 3.0.1
56
+ version: 2.2.2
57
+ - - <
58
+ - !ruby/object:Gem::Version
59
+ version: '3.1'
60
+ none: false
58
61
  type: :runtime
59
- prerelease: false
60
62
  version_requirements: !ruby/object:Gem::Requirement
61
- none: false
62
63
  requirements:
63
- - - '='
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: 2.2.2
67
+ - - <
64
68
  - !ruby/object:Gem::Version
65
- version: 3.0.1
69
+ version: '3.1'
70
+ none: false
71
+ prerelease: false
66
72
  - !ruby/object:Gem::Dependency
67
73
  name: hiredis
68
74
  requirement: !ruby/object:Gem::Requirement
69
- none: false
70
75
  requirements:
71
76
  - - '='
72
77
  - !ruby/object:Gem::Version
73
78
  version: 0.4.5
79
+ none: false
74
80
  type: :runtime
75
- prerelease: false
76
81
  version_requirements: !ruby/object:Gem::Requirement
77
- none: false
78
82
  requirements:
79
83
  - - '='
80
84
  - !ruby/object:Gem::Version
81
85
  version: 0.4.5
86
+ none: false
87
+ prerelease: false
82
88
  - !ruby/object:Gem::Dependency
83
89
  name: amq-client
84
90
  requirement: !ruby/object:Gem::Requirement
85
- none: false
86
91
  requirements:
87
92
  - - '='
88
93
  - !ruby/object:Gem::Version
89
- version: 0.9.10
94
+ version: 0.9.12
95
+ none: false
90
96
  type: :runtime
91
- prerelease: false
92
97
  version_requirements: !ruby/object:Gem::Requirement
93
- none: false
94
98
  requirements:
95
99
  - - '='
96
100
  - !ruby/object:Gem::Version
97
- version: 0.9.10
101
+ version: 0.9.12
102
+ none: false
103
+ prerelease: false
98
104
  - !ruby/object:Gem::Dependency
99
105
  name: amq-protocol
100
106
  requirement: !ruby/object:Gem::Requirement
101
- none: false
102
107
  requirements:
103
108
  - - '='
104
109
  - !ruby/object:Gem::Version
105
- version: 1.0.1
110
+ version: 1.2.0
111
+ none: false
106
112
  type: :runtime
107
- prerelease: false
108
113
  version_requirements: !ruby/object:Gem::Requirement
109
- none: false
110
114
  requirements:
111
115
  - - '='
112
116
  - !ruby/object:Gem::Version
113
- version: 1.0.1
117
+ version: 1.2.0
118
+ none: false
119
+ prerelease: false
114
120
  - !ruby/object:Gem::Dependency
115
121
  name: amqp
116
122
  requirement: !ruby/object:Gem::Requirement
117
- none: false
118
123
  requirements:
119
124
  - - '='
120
125
  - !ruby/object:Gem::Version
121
- version: 0.9.8
126
+ version: 0.9.10
127
+ none: false
122
128
  type: :runtime
123
- prerelease: false
124
129
  version_requirements: !ruby/object:Gem::Requirement
125
- none: false
126
130
  requirements:
127
131
  - - '='
128
132
  - !ruby/object:Gem::Version
129
- version: 0.9.8
133
+ version: 0.9.10
134
+ none: false
135
+ prerelease: false
130
136
  - !ruby/object:Gem::Dependency
131
137
  name: activesupport
132
138
  requirement: !ruby/object:Gem::Requirement
133
- none: false
134
139
  requirements:
135
140
  - - ! '>='
136
141
  - !ruby/object:Gem::Version
137
142
  version: 2.3.4
143
+ none: false
138
144
  type: :runtime
139
- prerelease: false
140
145
  version_requirements: !ruby/object:Gem::Requirement
141
- none: false
142
146
  requirements:
143
147
  - - ! '>='
144
148
  - !ruby/object:Gem::Version
145
149
  version: 2.3.4
150
+ none: false
151
+ prerelease: false
146
152
  - !ruby/object:Gem::Dependency
147
153
  name: eventmachine_httpserver
148
154
  requirement: !ruby/object:Gem::Requirement
149
- none: false
150
155
  requirements:
151
156
  - - ! '>='
152
157
  - !ruby/object:Gem::Version
153
158
  version: 0.2.1
159
+ none: false
154
160
  type: :runtime
155
- prerelease: false
156
161
  version_requirements: !ruby/object:Gem::Requirement
157
- none: false
158
162
  requirements:
159
163
  - - ! '>='
160
164
  - !ruby/object:Gem::Version
161
165
  version: 0.2.1
166
+ none: false
167
+ prerelease: false
162
168
  - !ruby/object:Gem::Dependency
163
169
  name: daemons
164
170
  requirement: !ruby/object:Gem::Requirement
165
- none: false
166
171
  requirements:
167
172
  - - ! '>='
168
173
  - !ruby/object:Gem::Version
169
174
  version: 1.0.10
175
+ none: false
170
176
  type: :runtime
171
- prerelease: false
172
177
  version_requirements: !ruby/object:Gem::Requirement
173
- none: false
174
178
  requirements:
175
179
  - - ! '>='
176
180
  - !ruby/object:Gem::Version
177
181
  version: 1.0.10
182
+ none: false
183
+ prerelease: false
178
184
  description: A highly available, reliable messaging infrastructure
179
185
  email: opensource@xing.com
180
186
  executables:
@@ -270,23 +276,23 @@ rdoc_options:
270
276
  require_paths:
271
277
  - lib
272
278
  required_ruby_version: !ruby/object:Gem::Requirement
273
- none: false
274
279
  requirements:
275
280
  - - ! '>='
276
281
  - !ruby/object:Gem::Version
277
- version: '0'
278
282
  segments:
279
283
  - 0
280
- hash: -3293468122773492105
281
- required_rubygems_version: !ruby/object:Gem::Requirement
284
+ hash: -863009242128424125
285
+ version: '0'
282
286
  none: false
287
+ required_rubygems_version: !ruby/object:Gem::Requirement
283
288
  requirements:
284
289
  - - ! '>='
285
290
  - !ruby/object:Gem::Version
286
291
  version: 1.3.7
292
+ none: false
287
293
  requirements: []
288
294
  rubyforge_project:
289
- rubygems_version: 1.8.24
295
+ rubygems_version: 1.8.25
290
296
  signing_key:
291
297
  specification_version: 3
292
298
  summary: High Availability AMQP Messaging with Redundant Queues