bunny 1.1.0.pre1 → 1.1.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 840a6fefa94a5fef22bdc0a24178ba01498160bb
4
- data.tar.gz: 98b792aa4957d2109ce984742f7cb50551b6503d
3
+ metadata.gz: 843f81c989d0b413c14b8874bb6f2e4365da3921
4
+ data.tar.gz: b4fcd0d7a8e3effe884f2b3ecefc2db524aaf545
5
5
  SHA512:
6
- metadata.gz: 2f50640dcc07103b1f4d6c54230d27ad658bc8b6870874d781718cfca9910abca7c489bbd44acfb8f6f42a237ddf24afe7c484d6a36ab820d18ad846d1ae5585
7
- data.tar.gz: 40d9d577e9b65e43882011714cf5f956804818fc692b7d30adc5e131e97ee51ab5b1bcee07434dceb097a571436917bc602078aed331594b32231ef54a8bb2cc
6
+ metadata.gz: dee6d9086ff1654324c354037d1a35875b2ded96a1b0701c9836d5d2e4d41ff83df212ac5efca7643e50c30304f0be360ddbd4f6d64470634348b625a71f0016
7
+ data.tar.gz: 3fd0c4d92be3121789bf4dc7349583c24de9534cafb3a686fef5e080211c708a02ba15aef7e555c29c2dec2567439f8dcc11c6889da0788e272703b355b3ac28
@@ -1,5 +1,38 @@
1
+ ## Changes between Bunny 1.1.0.pre1 and 1.1.0.pre2
2
+
3
+ ### connection.tune.channel_max No Longer Overflows
4
+
5
+ `connection.tune.channel_max` could previously be configured to values
6
+ greater than 2^16 - 1 (65535). This would result in a silent overflow
7
+ during serialization. The issue was harmless in practice but is still
8
+ a bug that can be quite confusing.
9
+
10
+ Bunny now caps max number of channels to 65535. This allows it to be
11
+ forward compatible with future RabbitMQ versions that may allow limiting
12
+ total # of open channels via server configuration.
13
+
14
+ ### amq-protocol Update
15
+
16
+ Minimum `amq-protocol` version is now `1.9.0` which includes
17
+ bug fixes and performance improvements for channel ID allocator.
18
+
19
+ ### Thread Leaks Fixes
20
+
21
+ Bunny will now correctly release heartbeat sender when allocating
22
+ a new one (usually happens only when connection recovers from a network
23
+ failure).
24
+
25
+
1
26
  ## Changes between Bunny 1.0.0 and 1.1.0.pre1
2
27
 
28
+ ### Versioned Delivery Tag Fix
29
+
30
+ Versioned delivery tag now ensures all the arguments it operates
31
+ (original delivery tag, atomic fixnum instances, etc) are coerced to `Integer`
32
+ before comparison.
33
+
34
+ GitHub issues: #171.
35
+
3
36
  ### User-Provided Loggers
4
37
 
5
38
  Bunny now can use any logger that provides the same API as Ruby standard library's `Logger`:
data/README.md CHANGED
@@ -48,7 +48,7 @@ Specific examples:
48
48
 
49
49
  Bunny 0.9 and more recent versions support
50
50
 
51
- * CRuby 1.9.3, 1.9.2, 2.0.0, and 1.8.7
51
+ * CRuby 2.0.0, 1.9.3, 1.9.2, and 1.8.7
52
52
  * JRuby 1.7+
53
53
  * Rubinius 2.0+
54
54
 
@@ -92,7 +92,7 @@ gem install bunny
92
92
  To use Bunny in a project managed with Bundler:
93
93
 
94
94
  ``` ruby
95
- gem "bunny", ">= 1.0.0.rc2"
95
+ gem "bunny", "~> 1.0.4"
96
96
  ```
97
97
 
98
98
 
@@ -29,7 +29,7 @@ Gem::Specification.new do |s|
29
29
  map { |mail| Base64.decode64(mail) }
30
30
 
31
31
  # Dependencies
32
- s.add_dependency "amq-protocol", ">= 1.8.0"
32
+ s.add_dependency "amq-protocol", ">= 1.9.0"
33
33
 
34
34
  # Files.
35
35
  s.has_rdoc = true
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "bundler"
5
+ Bundler.setup
6
+
7
+ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
+
9
+ require 'Bunny'
10
+
11
+ conn = Bunny.new(:heartbeat_interval => 8)
12
+ conn.start
13
+
14
+ ch0 = conn.create_channel
15
+ ch1 = conn.create_channel
16
+ ch2 = conn.create_channel
17
+ ch3 = conn.create_channel
18
+
19
+ x = ch1.topic("hb.examples.recovery.topic", :durable => false)
20
+ q1 = ch1.queue("hb.examples.recovery.client_named_queue1", :durable => false)
21
+ q2 = ch2.queue("hb.examples.recovery.client_named_queue2", :durable => false)
22
+ q3 = ch3.queue("hb.examples.recovery.client_named_queue3", :durable => false)
23
+
24
+ q1.bind(x, :routing_key => "abc")
25
+ q2.bind(x, :routing_key => "def")
26
+ q3.bind(x, :routing_key => "xyz")
27
+
28
+ x0 = ch0.fanout("hb.examples.recovery.fanout0")
29
+ x1 = ch1.fanout("hb.examples.recovery.fanout1")
30
+ x2 = ch2.fanout("hb.examples.recovery.fanout2")
31
+ x3 = ch3.fanout("hb.examples.recovery.fanout3")
32
+
33
+ q4 = ch1.queue("", :exclusive => true)
34
+ q4.bind(x0)
35
+
36
+ q5 = ch2.queue("", :exclusive => true)
37
+ q5.bind(x1)
38
+
39
+ q6 = ch3.queue("", :exclusive => true)
40
+ q6.bind(x2)
41
+ q6.bind(x3)
42
+
43
+
44
+ q1.subscribe do |delivery_info, metadata, payload|
45
+ puts "[Q1] Consumed #{payload} on channel #{q1.channel.id}"
46
+ if ch0.open?
47
+ puts "Publishing a reply on channel #{ch0.id} which is open"
48
+ x0.publish(Time.now.to_i.to_s)
49
+ end
50
+ end
51
+
52
+ q2.subscribe do |delivery_info, metadata, payload|
53
+ puts "[Q2] Consumed #{payload} on channel #{q2.channel.id}"
54
+
55
+ if ch1.open?
56
+ puts "Publishing a reply on channel #{ch1.id} which is open"
57
+ x1.publish(Time.now.to_i.to_s)
58
+ end
59
+ end
60
+
61
+ q3.subscribe do |delivery_info, metadata, payload|
62
+ puts "[Q3] Consumed #{payload} (consumer 1, channel #{q3.channel.id})"
63
+
64
+ if ch2.open?
65
+ puts "Publishing a reply on channel #{ch1.id} which is open"
66
+ x2.publish(Time.now.to_i.to_s)
67
+ end
68
+ end
69
+
70
+ q3.subscribe do |delivery_info, metadata, payload|
71
+ puts "[Q3] Consumed #{payload} (consumer 2, channel #{q3.channel.id})"
72
+
73
+ if ch3.open?
74
+ puts "Publishing a reply on channel #{ch3.id} which is open"
75
+ x3.publish(Time.now.to_i.to_s)
76
+ end
77
+ end
78
+
79
+ q4.subscribe do |delivery_info, metadata, payload|
80
+ puts "[Q4] Consumed #{payload} on channel #{q4.channel.id}"
81
+ end
82
+
83
+ q5.subscribe do |delivery_info, metadata, payload|
84
+ puts "[Q5] Consumed #{payload} on channel #{q5.channel.id}"
85
+ end
86
+
87
+ q6.subscribe do |delivery_info, metadata, payload|
88
+ puts "[Q6] Consumed #{payload} on channel #{q6.channel.id}"
89
+ end
90
+
91
+ loop do
92
+ sleep 1
93
+ data = rand.to_s
94
+ rk = ["abc", "def", "xyz", Time.now.to_i.to_s].sample
95
+
96
+ begin
97
+ 3.times do
98
+ x.publish(rand.to_s, :routing_key => rk)
99
+ puts "Published #{data}, routing key: #{rk} on channel #{x.channel.id}"
100
+ end
101
+ # happens when a message is published before the connection
102
+ # is recovered
103
+ rescue Exception => e
104
+ puts "Exception: #{e.message}"
105
+ # e.backtrace.each do |line|
106
+ # puts "\t#{line}"
107
+ # end
108
+ end
109
+ end
@@ -42,6 +42,18 @@ module Bunny
42
42
  end
43
43
  end
44
44
 
45
+ # Can indicate either a channel or connection-level issue
46
+ class NotAllowedError < Exception
47
+ attr_reader :connection, :connection_close
48
+
49
+ def initialize(message, connection, connection_close = nil)
50
+ super(message)
51
+
52
+ @connection = connection
53
+ @connection_close = connection_close
54
+ end
55
+ end
56
+
45
57
 
46
58
  # Raised when TCP connection to RabbitMQ fails because of an unresolved
47
59
  # hostname, connectivity problem, etc
@@ -36,6 +36,10 @@ module Bunny
36
36
  DEFAULT_HEARTBEAT = :server
37
37
  # @private
38
38
  DEFAULT_FRAME_MAX = 131072
39
+ # 2^16 - 1, maximum representable signed 16 bit integer.
40
+ # @private
41
+ CHANNEL_MAX_LIMIT = 65535
42
+ DEFAULT_CHANNEL_MAX = CHANNEL_MAX_LIMIT
39
43
 
40
44
  # backwards compatibility
41
45
  # @private
@@ -78,7 +82,7 @@ module Bunny
78
82
 
79
83
  # @return [Bunny::Transport]
80
84
  attr_reader :transport
81
- attr_reader :status, :host, :port, :heartbeat, :user, :pass, :vhost, :frame_max, :threaded
85
+ attr_reader :status, :host, :port, :heartbeat, :user, :pass, :vhost, :frame_max, :channel_max, :threaded
82
86
  attr_reader :server_capabilities, :server_properties, :server_authentication_mechanisms, :server_locales
83
87
  attr_reader :default_channel
84
88
  attr_reader :channel_id_allocator
@@ -150,7 +154,9 @@ module Bunny
150
154
 
151
155
  # these are negotiated with the broker during the connection tuning phase
152
156
  @client_frame_max = opts.fetch(:frame_max, DEFAULT_FRAME_MAX)
153
- @client_channel_max = opts.fetch(:channel_max, 65536)
157
+ @client_channel_max = normalize_client_channel_max(opts.fetch(:channel_max, DEFAULT_CHANNEL_MAX))
158
+ # will be-renegotiated during connection tuning steps. MK.
159
+ @channel_max = @client_channel_max
154
160
  @client_heartbeat = self.heartbeat_from(opts)
155
161
 
156
162
  @client_properties = opts[:properties] || DEFAULT_CLIENT_PROPERTIES
@@ -288,9 +294,10 @@ module Bunny
288
294
 
289
295
  maybe_shutdown_reader_loop
290
296
  close_transport
291
-
292
- @status = :closed
293
297
  end
298
+
299
+ shut_down_all_consumer_work_pools!
300
+ @status = :closed
294
301
  end
295
302
  alias stop close
296
303
 
@@ -480,13 +487,14 @@ module Bunny
480
487
  if @transport.open?
481
488
  @transport.send_frame(AMQ::Protocol::Connection::Close.encode(200, "Goodbye", 0, 0))
482
489
 
483
- maybe_shutdown_heartbeat_sender
484
- @status = :not_connected
485
-
486
490
  if sync
487
491
  @last_connection_close_ok = wait_on_continuations
488
492
  end
489
493
  end
494
+
495
+ shut_down_all_consumer_work_pools!
496
+ maybe_shutdown_heartbeat_sender
497
+ @status = :not_connected
490
498
  end
491
499
 
492
500
  # Handles incoming frames and dispatches them.
@@ -508,6 +516,17 @@ module Bunny
508
516
  @last_connection_error = instantiate_connection_level_exception(method)
509
517
  @continuations.push(method)
510
518
 
519
+ begin
520
+ shut_down_all_consumer_work_pools!
521
+ maybe_shutdown_reader_loop
522
+ rescue ShutdownSignal => sse
523
+ # no-op
524
+ rescue Exception => e
525
+ @logger.warn "Caught an exception when cleaning up after receiving connection.close: #{e.message}"
526
+ ensure
527
+ close_transport
528
+ end
529
+
511
530
  @origin_thread.raise(@last_connection_error)
512
531
  when AMQ::Protocol::Connection::CloseOk then
513
532
  @last_connection_close_ok = method
@@ -647,6 +666,8 @@ module Bunny
647
666
  UnexpectedFrame
648
667
  when 506 then
649
668
  ResourceError
669
+ when 530 then
670
+ NotAllowedError
650
671
  when 541 then
651
672
  InternalError
652
673
  else
@@ -938,7 +959,25 @@ module Bunny
938
959
  initialize_heartbeat_sender
939
960
  end
940
961
 
941
- raise "could not open connection: server did not respond with connection.open-ok" unless connection_open_ok.is_a?(AMQ::Protocol::Connection::OpenOk)
962
+ unless connection_open_ok.is_a?(AMQ::Protocol::Connection::OpenOk)
963
+ if connection_open_ok.is_a?(AMQ::Protocol::Connection::Close)
964
+ e = instantiate_connection_level_exception(connection_open_ok)
965
+ begin
966
+ shut_down_all_consumer_work_pools!
967
+ maybe_shutdown_reader_loop
968
+ rescue ShutdownSignal => sse
969
+ # no-op
970
+ rescue Exception => e
971
+ @logger.warn "Caught an exception when cleaning up after receiving connection.close: #{e.message}"
972
+ ensure
973
+ close_transport
974
+ end
975
+
976
+ @origin_thread.raise(e)
977
+ else
978
+ raise "could not open connection: server did not respond with connection.open-ok but #{connection_open_ok.inspect} instead"
979
+ end
980
+ end
942
981
  end
943
982
 
944
983
  def heartbeat_disabled?(val)
@@ -958,6 +997,7 @@ module Bunny
958
997
 
959
998
  # @private
960
999
  def initialize_heartbeat_sender
1000
+ maybe_shutdown_heartbeat_sender
961
1001
  @logger.debug "Initializing heartbeat sender..."
962
1002
  @heartbeat_sender = HeartbeatSender.new(@transport, @logger)
963
1003
  @heartbeat_sender.start(@heartbeat)
@@ -1038,6 +1078,24 @@ module Bunny
1038
1078
  Logger::WARN
1039
1079
  end
1040
1080
  end
1081
+
1082
+ # @private
1083
+ def shut_down_all_consumer_work_pools!
1084
+ @channels.each do |_, ch|
1085
+ ch.maybe_kill_consumer_work_pool!
1086
+ end
1087
+ end
1088
+
1089
+ def normalize_client_channel_max(n)
1090
+ return CHANNEL_MAX_LIMIT if n > CHANNEL_MAX_LIMIT
1091
+
1092
+ case n
1093
+ when 0 then
1094
+ CHANNEL_MAX_LIMIT
1095
+ else
1096
+ n
1097
+ end
1098
+ end
1041
1099
  end # Session
1042
1100
 
1043
1101
  # backwards compatibility
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Bunny
4
4
  # @return [String] Version of the library
5
- VERSION = "1.1.0.pre1"
5
+ VERSION = "1.1.0.pre2"
6
6
  end
@@ -147,6 +147,25 @@ describe Bunny::Session do
147
147
  end
148
148
  end
149
149
 
150
+ context "initialized with :channel_max => 4096" do
151
+ after :each do
152
+ subject.close if subject.open?
153
+ end
154
+
155
+ let(:channel_max) { 1024 }
156
+ let(:subject) { described_class.new(:channel_max => channel_max) }
157
+
158
+ # this assumes RabbitMQ has no lower value configured. In 3.2
159
+ # it is 0 (no limit) by default and 1024 is still a fairly low value
160
+ # for future releases. MK.
161
+ it "negotiates channel max to be 1024" do
162
+ subject.start
163
+ subject.channel_max.should == channel_max
164
+
165
+ subject.close
166
+ end
167
+ end
168
+
150
169
  context "initialized with :ssl => true" do
151
170
  let(:subject) do
152
171
  described_class.new(:user => "bunny_gem",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bunny
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0.pre1
4
+ version: 1.1.0.pre2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Duncan
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2013-11-18 00:00:00.000000000 Z
15
+ date: 2013-11-25 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: amq-protocol
@@ -20,14 +20,14 @@ dependencies:
20
20
  requirements:
21
21
  - - '>='
22
22
  - !ruby/object:Gem::Version
23
- version: 1.8.0
23
+ version: 1.9.0
24
24
  type: :runtime
25
25
  prerelease: false
26
26
  version_requirements: !ruby/object:Gem::Requirement
27
27
  requirements:
28
28
  - - '>='
29
29
  - !ruby/object:Gem::Version
30
- version: 1.8.0
30
+ version: 1.9.0
31
31
  description: Easy to use, feature complete Ruby client for RabbitMQ 2.0 and later
32
32
  versions.
33
33
  email:
@@ -67,6 +67,7 @@ files:
67
67
  - examples/connection/automatic_recovery_with_basic_get.rb
68
68
  - examples/connection/automatic_recovery_with_client_named_queues.rb
69
69
  - examples/connection/automatic_recovery_with_multiple_consumers.rb
70
+ - examples/connection/automatic_recovery_with_republishing.rb
70
71
  - examples/connection/automatic_recovery_with_server_named_queues.rb
71
72
  - examples/connection/channel_level_exception.rb
72
73
  - examples/connection/disabled_automatic_recovery.rb
@@ -224,7 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
224
225
  version: 1.3.1
225
226
  requirements: []
226
227
  rubyforge_project:
227
- rubygems_version: 2.1.6
228
+ rubygems_version: 2.1.11
228
229
  signing_key:
229
230
  specification_version: 4
230
231
  summary: Popular easy to use Ruby client for RabbitMQ