bunny 1.1.0.pre1 → 1.1.0.pre2
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.
- checksums.yaml +4 -4
- data/ChangeLog.md +33 -0
- data/README.md +2 -2
- data/bunny.gemspec +1 -1
- data/examples/connection/automatic_recovery_with_republishing.rb +109 -0
- data/lib/bunny/exceptions.rb +12 -0
- data/lib/bunny/session.rb +66 -8
- data/lib/bunny/version.rb +1 -1
- data/spec/higher_level_api/integration/connection_spec.rb +19 -0
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 843f81c989d0b413c14b8874bb6f2e4365da3921
|
4
|
+
data.tar.gz: b4fcd0d7a8e3effe884f2b3ecefc2db524aaf545
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dee6d9086ff1654324c354037d1a35875b2ded96a1b0701c9836d5d2e4d41ff83df212ac5efca7643e50c30304f0be360ddbd4f6d64470634348b625a71f0016
|
7
|
+
data.tar.gz: 3fd0c4d92be3121789bf4dc7349583c24de9534cafb3a686fef5e080211c708a02ba15aef7e555c29c2dec2567439f8dcc11c6889da0788e272703b355b3ac28
|
data/ChangeLog.md
CHANGED
@@ -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,
|
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", "
|
95
|
+
gem "bunny", "~> 1.0.4"
|
96
96
|
```
|
97
97
|
|
98
98
|
|
data/bunny.gemspec
CHANGED
@@ -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
|
data/lib/bunny/exceptions.rb
CHANGED
@@ -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
|
data/lib/bunny/session.rb
CHANGED
@@ -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,
|
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
|
-
|
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
|
data/lib/bunny/version.rb
CHANGED
@@ -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.
|
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-
|
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.
|
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.
|
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.
|
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
|