beetle 0.2.9.6 → 0.2.9.9

Sign up to get free protection for your applications and to get access to all the features.
data/RELEASE_NOTES.rdoc CHANGED
@@ -1,10 +1,24 @@
1
1
  = Release Notes
2
2
 
3
+ == Version 0.2.9.8
4
+
5
+ * since version 2.0, RabbitMQ supports Basic.reject(:requeue => true). we use it now too,
6
+ because it enhances performance of message processors. this means of course, you can
7
+ only use beetle gem versions >= 0.2.9.8 if your rabbitmq brokers are at least version 2.0.
8
+ * publishing timeout defaults to 0 to avoid extreme message loss in some cases
9
+
10
+
11
+ == Version 0.2.9.7
12
+
13
+ * use new bunny_ext gem and allow specification of global publishing timeouts
14
+ * registering a message now automatically registers the corresponding exchange
15
+ * don't try to bind queues for an exchange hich has no queue
16
+ * ruby 1.9.2 compatibility fixes
17
+
3
18
  == Version 0.2.9
4
19
 
5
20
  * Beetle::Client now raises an exception when it fails to publish a message to at least 1 RabbitMQ server
6
21
  * Subscribers are now stopped cleanly to avoid 'closed abruptly' messages in the RabbitMQ server log
7
- * Added send and receive timeouts on the socket and use system_timer for ruby side timeouts
8
22
 
9
23
  == Version 0.2.6
10
24
 
data/beetle.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "beetle"
3
- s.version = "0.2.9.6"
3
+ s.version = "0.2.9.9"
4
4
 
5
5
  s.required_rubygems_version = ">= 1.3.1"
6
6
  s.authors = ["Stefan Kaes", "Pascal Friederich", "Ali Jelveh", "Sebastian Roebke"]
@@ -31,7 +31,7 @@ Gem::Specification.new do |s|
31
31
  s.specification_version = 3
32
32
  s.add_runtime_dependency("uuid4r", [">= 0.1.1"])
33
33
  s.add_runtime_dependency("bunny", ["= 0.6.0"])
34
- s.add_runtime_dependency("bunny-ext", [">= 0.6.2"])
34
+ s.add_runtime_dependency("bunny-ext", [">= 0.6.5"])
35
35
  s.add_runtime_dependency("redis", ["= 2.0.4"])
36
36
  s.add_runtime_dependency("amqp", [">= 0.6.7"])
37
37
  s.add_runtime_dependency("activesupport", ["~> 2.3.4"])
@@ -47,7 +47,8 @@ module Beetle
47
47
  # the password to use when connectiong to the AMQP servers (defaults to <tt>"guest"</tt>)
48
48
  attr_accessor :password
49
49
 
50
- # the socket timeout in seconds for message publishing (defaults to <tt>10.0</tt>)
50
+ # the socket timeout in seconds for message publishing (defaults to <tt>0</tt>).
51
+ # consider this a highly experimental feature for now.
51
52
  attr_accessor :publishing_timeout
52
53
 
53
54
  # external config file (defaults to <tt>no file</tt>)
@@ -73,7 +74,7 @@ module Beetle
73
74
  self.user = "guest"
74
75
  self.password = "guest"
75
76
 
76
- self.publishing_timeout = 5.0
77
+ self.publishing_timeout = 0
77
78
 
78
79
  self.log_file = STDOUT
79
80
  end
@@ -44,8 +44,8 @@ module Beetle
44
44
  exchange(exchange_name).publish(data, opts)
45
45
  logger.debug "Beetle: message sent!"
46
46
  published = 1
47
- rescue *bunny_exceptions
48
- stop!
47
+ rescue *bunny_exceptions => e
48
+ stop!(e)
49
49
  tries -= 1
50
50
  # retry same server on receiving the first exception for it (might have been a normal restart)
51
51
  # in this case you'll see either a broken pipe or a forced connection shutdown error
@@ -76,8 +76,8 @@ module Beetle
76
76
  exchange(exchange_name).publish(data, opts)
77
77
  published << @server
78
78
  logger.debug "Beetle: message sent (#{published})!"
79
- rescue *bunny_exceptions
80
- stop!
79
+ rescue *bunny_exceptions => e
80
+ stop!(e)
81
81
  retry if (tries += 1) == 1
82
82
  mark_server_dead
83
83
  end
@@ -120,8 +120,8 @@ module Beetle
120
120
  status = msg[:header].properties[:headers][:status]
121
121
  end
122
122
  logger.debug "Beetle: rpc complete!"
123
- rescue *bunny_exceptions
124
- stop!
123
+ rescue *bunny_exceptions => e
124
+ stop!(e)
125
125
  mark_server_dead
126
126
  tries -= 1
127
127
  retry if tries > 0
@@ -200,10 +200,17 @@ module Beetle
200
200
  queue
201
201
  end
202
202
 
203
- def stop!
203
+ def stop!(exception=nil)
204
204
  begin
205
- bunny.stop
206
- rescue Exception
205
+ Beetle::Timer.timeout(1) do
206
+ if exception
207
+ bunny.__send__ :close_socket
208
+ else
209
+ bunny.stop
210
+ end
211
+ end
212
+ rescue Exception => e
213
+ logger.error "Beetle: error closing down bunny #{e}"
207
214
  Beetle::reraise_expectation_errors!
208
215
  ensure
209
216
  @bunnies[@server] = nil
data/lib/beetle/r_c.rb CHANGED
@@ -4,7 +4,7 @@ module Beetle
4
4
  # message processing result return codes
5
5
  class ReturnCode
6
6
  def initialize(*args)
7
- @recover = args.delete :recover
7
+ @reject = args.delete :reject
8
8
  @failure = args.delete :failure
9
9
  @name = args.first
10
10
  end
@@ -13,8 +13,8 @@ module Beetle
13
13
  @name.blank? ? super : "Beetle::RC::#{@name}"
14
14
  end
15
15
 
16
- def recover?
17
- @recover
16
+ def reject?
17
+ @reject
18
18
  end
19
19
 
20
20
  def failure?
@@ -30,11 +30,11 @@ module Beetle
30
30
  rc :Ancient
31
31
  rc :AttemptsLimitReached, :failure
32
32
  rc :ExceptionsLimitReached, :failure
33
- rc :Delayed, :recover
34
- rc :HandlerCrash, :recover
35
- rc :HandlerNotYetTimedOut, :recover
36
- rc :MutexLocked, :recover
37
- rc :InternalError, :recover
33
+ rc :Delayed, :reject
34
+ rc :HandlerCrash, :reject
35
+ rc :HandlerNotYetTimedOut, :reject
36
+ rc :MutexLocked, :reject
37
+ rc :InternalError, :reject
38
38
  rc :DecodingError, :failure
39
39
 
40
40
  end
@@ -78,7 +78,7 @@ module Beetle
78
78
  def client_started(payload)
79
79
  id = payload["id"]
80
80
  if client_id_valid?(id)
81
- logger.info("Received client_started message from id #{id}")
81
+ logger.info("Received client_started message from id '#{id}'")
82
82
  else
83
83
  msg = "Received client_started message from unknown id '#{id}'"
84
84
  logger.error(msg)
@@ -1,3 +1,6 @@
1
+ require 'amqp'
2
+ require 'mq'
3
+
1
4
  module Beetle
2
5
  # Manages subscriptions and message processing on the receiver side of things.
3
6
  class Subscriber < Base
@@ -55,7 +58,7 @@ module Beetle
55
58
  end
56
59
 
57
60
  def queues_for_exchanges(exchanges)
58
- @client.exchanges.slice(*exchanges).map{|_, opts| opts[:queues]}.flatten.uniq
61
+ @client.exchanges.slice(*exchanges).map{|_, opts| opts[:queues]}.flatten.compact.uniq
59
62
  end
60
63
 
61
64
  def create_exchanges(exchanges)
@@ -107,9 +110,9 @@ module Beetle
107
110
  message_options = opts.merge(:server => server, :store => @client.deduplication_store)
108
111
  m = Message.new(amqp_queue_name, header, data, message_options)
109
112
  result = m.process(processor)
110
- if result.recover?
113
+ if result.reject?
111
114
  sleep 1
112
- mq(server).recover
115
+ header.reject(:requeue => true)
113
116
  elsif reply_to = header.properties[:reply_to]
114
117
  # require 'ruby-debug'
115
118
  # Debugger.start
@@ -122,9 +125,6 @@ module Beetle
122
125
  Beetle::reraise_expectation_errors!
123
126
  # swallow all exceptions
124
127
  logger.error "Beetle: internal error during message processing: #{$!}: #{$!.backtrace.join("\n")}"
125
- ensure
126
- # support amqp metrics logging if we have it
127
- logger.agent.flush if logger.respond_to?(:agent)
128
128
  end
129
129
  end
130
130
  end
data/lib/beetle.rb CHANGED
@@ -1,6 +1,4 @@
1
1
  $:.unshift(File.expand_path('..', __FILE__))
2
- require 'amqp'
3
- require 'mq'
4
2
  require 'bunny-ext'
5
3
  require 'uuid4r'
6
4
  require 'active_support'
@@ -543,7 +543,7 @@ module Beetle
543
543
  handler.expects(:process_failure).never
544
544
  result = message.process(handler)
545
545
  assert_equal RC::HandlerCrash, result
546
- assert result.recover?
546
+ assert result.reject?
547
547
  assert !result.failure?
548
548
  end
549
549
 
@@ -561,7 +561,7 @@ module Beetle
561
561
  failback.expects(:call).once
562
562
  result = message.process(handler)
563
563
  assert_equal RC::AttemptsLimitReached, result
564
- assert !result.recover?
564
+ assert !result.reject?
565
565
  assert result.failure?
566
566
  end
567
567
 
@@ -578,7 +578,7 @@ module Beetle
578
578
  failback.expects(:call).once
579
579
  result = message.process(handler)
580
580
  assert_equal RC::ExceptionsLimitReached, result
581
- assert !result.recover?
581
+ assert !result.reject?
582
582
  assert result.failure?
583
583
  end
584
584
 
@@ -19,7 +19,7 @@ module Beetle
19
19
  m = mock("dummy")
20
20
  expected_bunny_options = {
21
21
  :host => @pub.send(:current_host), :port => @pub.send(:current_port),
22
- :logging => false, :user => "guest", :pass => "guest", :vhost => "/", :socket_timeout => 5.0
22
+ :logging => false, :user => "guest", :pass => "guest", :vhost => "/", :socket_timeout => 0
23
23
  }
24
24
  Bunny.expects(:new).with(expected_bunny_options).returns(m)
25
25
  m.expects(:start)
@@ -189,6 +189,27 @@ module Beetle
189
189
  @pub.expects(:publish_with_failover).with("mama-exchange", "mama", @data, @opts).returns(1)
190
190
  assert_equal 1, @pub.publish("mama", @data)
191
191
  end
192
+
193
+ test "failover publishing should raise an exception if the message was published to no server" do
194
+ failover = sequence("failover")
195
+ @pub.servers = ["someserver", "someotherserver"]
196
+ @pub.server = "someserver"
197
+
198
+ e = mock("exchange")
199
+ @pub.expects(:exchange).with("mama-exchange").returns(e).in_sequence(failover)
200
+ e.expects(:publish).raises(Bunny::ConnectionError).in_sequence(failover)
201
+ @pub.expects(:exchange).with("mama-exchange").returns(e).in_sequence(failover)
202
+ e.expects(:publish).raises(Bunny::ConnectionError).in_sequence(failover)
203
+ @pub.expects(:exchange).with("mama-exchange").returns(e).in_sequence(failover)
204
+ e.expects(:publish).raises(Bunny::ConnectionError).in_sequence(failover)
205
+ @pub.expects(:exchange).with("mama-exchange").returns(e).in_sequence(failover)
206
+ e.expects(:publish).raises(Bunny::ConnectionError).in_sequence(failover)
207
+
208
+ assert_raises Beetle::NoMessageSent do
209
+ @pub.publish_with_failover("mama-exchange", "mama", @data, @opts)
210
+ end
211
+ end
212
+
192
213
  end
193
214
 
194
215
  class PublisherQueueManagementTest < Test::Unit::TestCase
@@ -300,5 +300,14 @@ module Beetle
300
300
  @server.logger.expects(:error).with(msg)
301
301
  @server.client_started(payload)
302
302
  end
303
+
304
+ test "should log an info about received client_started client_started messages" do
305
+ payload = {"id" => "known-client"}
306
+ msg = "Received client_started message from id 'known-client'"
307
+ @server.logger.expects(:info).with(msg)
308
+ @server.expects(:client_id_valid?).with('known-client').returns(true)
309
+ @server.client_started(payload)
310
+ end
311
+
303
312
  end
304
313
  end
@@ -44,7 +44,12 @@ module Beetle
44
44
  assert_equal 42, mqs[@sub.server]
45
45
  end
46
46
 
47
- test "stop! should stop the event loop" do
47
+ test "stop! should close all amqp connections and then stop the event loop" do
48
+ connection1 = mock('con1')
49
+ connection1.expects(:close).yields
50
+ connection2 = mock('con2')
51
+ connection2.expects(:close).yields
52
+ @sub.instance_variable_set "@amqp_connections", [["server1", connection1], ["server2",connection2]]
48
53
  EM.expects(:stop_event_loop)
49
54
  @sub.send(:stop!)
50
55
  end
@@ -103,6 +108,10 @@ module Beetle
103
108
  @sub.send(:bind_queues, %W(x y))
104
109
  end
105
110
 
111
+ test "should not try to bind a queue for an exchange which has no queue" do
112
+ @client.register_message(:without_queue)
113
+ assert_equal [], @sub.send(:queues_for_exchanges, ["without_queue"])
114
+ end
106
115
  end
107
116
 
108
117
  class SubscriberExchangeManagementTest < Test::Unit::TestCase
@@ -160,15 +169,13 @@ module Beetle
160
169
  assert_nothing_raised { @callback.call(header, 'foo') }
161
170
  end
162
171
 
163
- test "should call recover on the server when processing the handler returns true on recover?" do
172
+ test "should call reject on the message header when processing the handler returns true on recover?" do
164
173
  header = header_with_params({})
165
174
  result = mock("result")
166
- result.expects(:recover?).returns(true)
175
+ result.expects(:reject?).returns(true)
167
176
  Message.any_instance.expects(:process).returns(result)
168
177
  @sub.expects(:sleep).with(1)
169
- mq = mock("MQ")
170
- mq.expects(:recover)
171
- @sub.expects(:mq).with(@sub.server).returns(mq)
178
+ header.expects(:reject).with(:requeue => true)
172
179
  @callback.call(header, 'foo')
173
180
  end
174
181
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: beetle
3
3
  version: !ruby/object:Gem::Version
4
- hash: 119
4
+ hash: 105
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
9
  - 9
10
- - 6
11
- version: 0.2.9.6
10
+ - 9
11
+ version: 0.2.9.9
12
12
  platform: ruby
13
13
  authors:
14
14
  - Stefan Kaes
@@ -19,7 +19,7 @@ autorequire:
19
19
  bindir: bin
20
20
  cert_chain: []
21
21
 
22
- date: 2010-12-01 00:00:00 +01:00
22
+ date: 2010-12-22 00:00:00 +01:00
23
23
  default_executable: beetle
24
24
  dependencies:
25
25
  - !ruby/object:Gem::Dependency
@@ -62,12 +62,12 @@ dependencies:
62
62
  requirements:
63
63
  - - ">="
64
64
  - !ruby/object:Gem::Version
65
- hash: 3
65
+ hash: 13
66
66
  segments:
67
67
  - 0
68
68
  - 6
69
- - 2
70
- version: 0.6.2
69
+ - 5
70
+ version: 0.6.5
71
71
  type: :runtime
72
72
  version_requirements: *id003
73
73
  - !ruby/object:Gem::Dependency