bunny 2.1.0 → 2.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4c95090be1c7c3802f91e9bbde8a65145be46036
4
- data.tar.gz: dba06fb978dcbdc3613e25dd6dc6d2ae6520e114
3
+ metadata.gz: 4513453ba83efb33a882a0360116c2d652deaeca
4
+ data.tar.gz: a5b1deb516b9a0798f02b394d78eabc3022e96ba
5
5
  SHA512:
6
- metadata.gz: 33990cab5ebb80edd6c95b33da3e1c1c572830acc8fa9a2d177340ca88fd9a40907c68507505736096eb8595d64ffa23fd560633d9f50edc3068e7ec55806035
7
- data.tar.gz: 209a580762f599be1a69ba24c6aab533891a54f5e55f82920e258bb886205c0e96849ad165b2a4a86f9bd3aed6be71f21b9aa4ecc5e6201edfb310396bf1380e
6
+ metadata.gz: 71f26786c44ac4580a48cacb604dbeed272bab27005bbb3d4dc0816e10d02679e23adcd671898a8a8f12727c0a63fec16a15dddf1764f16cd13bec8d8a867cd8
7
+ data.tar.gz: 3ac225508d9a2bc13b1e24f98bb879e8a4ce500340abff9362e28c1fe9e106eff44a5d6b818df65a432eb3f0d48db9ff6db4760c91041b16e9558ffc092dd6fe
@@ -1,3 +1,18 @@
1
+ ## Changes between Bunny 2.1.0 and 2.2.0
2
+
3
+ ### Add :addresses to connect options
4
+
5
+ Before this the connection options only allowed multiple hosts, an
6
+ address is a combination of a host and a port. This makes it possible to
7
+ specify different hosts with different ports.
8
+
9
+ ### Recover from connection.close by default
10
+
11
+ Bunny will now try to reconnect also when server sent connection.close is
12
+ received, e.g. when a server is restarting (but also when the connection is
13
+ force closed by the server). This is in-line with how many other clients behave.
14
+ The old default was `recover_from_connection_close: false`.
15
+
1
16
  ## Changes between Bunny 2.0.0 and 2.1.0
2
17
 
3
18
  Bunny 2.1.0 has an **important breaking change**. It is highly
data/README.md CHANGED
@@ -68,14 +68,6 @@ Bunny `1.4.x` and earlier supports RabbitMQ 2.x and 3.x.
68
68
  Bunny is a mature library (started in early 2009) with
69
69
  a stable public API.
70
70
 
71
- Before version 0.9, **a lot** of functionality was missing. Version
72
- 0.9 can be considered to be a "second birthday" for Bunny as it was
73
- rewritten from scratch with over a dozen of preview releases over the
74
- course of about a year.
75
-
76
- We (the maintainers) made our best effort to keep the new version as
77
- backwards compatible as possible but within reason.
78
-
79
71
 
80
72
  ## Installation & Bundler Dependency
81
73
 
@@ -96,7 +88,7 @@ gem install bunny
96
88
  To use Bunny in a project managed with Bundler:
97
89
 
98
90
  ``` ruby
99
- gem "bunny", ">= 2.0.1"
91
+ gem "bunny", ">= 2.1.0"
100
92
  ```
101
93
 
102
94
 
@@ -21,13 +21,7 @@ Gem::Specification.new do |s|
21
21
  "Michael S. Klishin",
22
22
  "Stefan Kaes"]
23
23
 
24
- s.email = [
25
- "Y2VsbGRlZUBnbWFpbC5jb20=\n",
26
- "ZXJpY0A1c3RvcHMuY29t\n",
27
- "c3Rhc3RueUAxMDFpZGVhcy5jeg==\n",
28
- "bWljaGFlbEBub3ZlbWJlcmFpbi5jb20=\n",
29
- "c2thZXNAcmFpbHNleHByZXNzLmRl\n"].
30
- map { |mail| Base64.decode64(mail) }
24
+ s.email = ["michael.s.klishin@gmail.com"]
31
25
 
32
26
  # Dependencies
33
27
  s.add_dependency "amq-protocol", ">= 2.0.0"
@@ -19,6 +19,7 @@ module Bunny
19
19
  def initialize(size = 1)
20
20
  @size = size
21
21
  @queue = ::Queue.new
22
+ @paused = false
22
23
  end
23
24
 
24
25
 
@@ -41,6 +42,14 @@ module Bunny
41
42
  @running
42
43
  end
43
44
 
45
+ def backlog
46
+ @queue.length
47
+ end
48
+
49
+ def busy?
50
+ !@queue.empty?
51
+ end
52
+
44
53
  def shutdown
45
54
  @running = false
46
55
 
@@ -57,12 +66,12 @@ module Bunny
57
66
 
58
67
  def pause
59
68
  @running = false
60
-
61
- @threads.each { |t| t.stop }
69
+ @paused = true
62
70
  end
63
71
 
64
72
  def resume
65
73
  @running = true
74
+ @paused = false
66
75
 
67
76
  @threads.each { |t| t.run }
68
77
  end
@@ -78,6 +87,7 @@ module Bunny
78
87
  def run_loop
79
88
  catch(:terminate) do
80
89
  loop do
90
+ Thread.stop if @paused
81
91
  callable = @queue.pop
82
92
 
83
93
  begin
@@ -95,6 +95,7 @@ module Bunny
95
95
  #
96
96
  # @option connection_string_or_opts [String] :host ("127.0.0.1") Hostname or IP address to connect to
97
97
  # @option connection_string_or_opts [Array<String>] :hosts (["127.0.0.1"]) list of hostname or IP addresses to select hostname from when connecting
98
+ # @option connection_string_or_opts [Array<String>] :addresses (["127.0.0.1:5672"]) list of addresses to select hostname and port from when connecting
98
99
  # @option connection_string_or_opts [Integer] :port (5672) Port RabbitMQ listens on
99
100
  # @option connection_string_or_opts [String] :username ("guest") Username
100
101
  # @option connection_string_or_opts [String] :password ("guest") Password
@@ -113,8 +114,9 @@ module Bunny
113
114
  # @option connection_string_or_opts [IO, String] :log_file The file or path to use when creating a logger. Defaults to STDOUT.
114
115
  # @option connection_string_or_opts [IO, String] :logfile DEPRECATED: use :log_file instead. The file or path to use when creating a logger. Defaults to STDOUT.
115
116
  # @option connection_string_or_opts [Integer] :log_level The log level to use when creating a logger. Defaults to LOGGER::WARN
116
- # @option connection_string_or_opts [Boolean] :automatically_recover Should automatically recover from network failures?
117
- # @option connection_string_or_opts [Integer] :recovery_attempts Max number of recovery attempts
117
+ # @option connection_string_or_opts [Boolean] :automatically_recover (true) Should automatically recover from network failures?
118
+ # @option connection_string_or_opts [Integer] :recovery_attempts (nil) Max number of recovery attempts, nil means forever, 0 means never
119
+ # @option connection_string_or_opts [Boolean] :recover_from_connection_close (true) Recover from server-sent connection.close
118
120
  #
119
121
  # @option optz [String] :auth_mechanism ("PLAIN") Authentication mechanism, PLAIN or EXTERNAL
120
122
  # @option optz [String] :locale ("PLAIN") Locale RabbitMQ should use
@@ -135,10 +137,9 @@ module Bunny
135
137
  @default_hosts_shuffle_strategy = Proc.new { |hosts| hosts.shuffle }
136
138
 
137
139
  @opts = opts
138
- @hosts = self.hostnames_from(opts)
139
- @host_index = 0
140
+ @addresses = self.addresses_from(opts)
141
+ @address_index = 0
140
142
 
141
- @port = self.port_from(opts)
142
143
  @user = self.username_from(opts)
143
144
  @pass = self.password_from(opts)
144
145
  @vhost = self.vhost_from(opts)
@@ -148,6 +149,8 @@ module Bunny
148
149
  log_level = opts[:log_level] || ENV["BUNNY_LOG_LEVEL"] || Logger::WARN
149
150
  @logger = opts.fetch(:logger, init_default_logger(log_file, log_level))
150
151
 
152
+ validate_connection_options(opts)
153
+
151
154
  # should automatic recovery from network failures be used?
152
155
  @automatically_recover = if opts[:automatically_recover].nil? && opts[:automatic_recovery].nil?
153
156
  true
@@ -156,7 +159,7 @@ module Bunny
156
159
  end
157
160
  @recovery_attempts = opts[:recovery_attempts]
158
161
  @network_recovery_interval = opts.fetch(:network_recovery_interval, DEFAULT_NETWORK_RECOVERY_INTERVAL)
159
- @recover_from_connection_close = opts.fetch(:recover_from_connection_close, false)
162
+ @recover_from_connection_close = opts.fetch(:recover_from_connection_close, true)
160
163
  # in ms
161
164
  @continuation_timeout = opts.fetch(:continuation_timeout, DEFAULT_CONTINUATION_TIMEOUT)
162
165
 
@@ -183,7 +186,7 @@ module Bunny
183
186
  # the non-reentrant Ruby mutexes. MK.
184
187
  @transport_mutex = @mutex_impl.new
185
188
  @status_mutex = @mutex_impl.new
186
- @host_index_mutex = @mutex_impl.new
189
+ @address_index_mutex = @mutex_impl.new
187
190
 
188
191
  @channels = Hash.new
189
192
 
@@ -193,6 +196,16 @@ module Bunny
193
196
  self.initialize_transport
194
197
  end
195
198
 
199
+ def validate_connection_options(options)
200
+ if options[:hosts] && options[:addresses]
201
+ raise ArgumentError, "Connection options can't contain hosts and addresses at the same time"
202
+ end
203
+
204
+ if (options[:host] || options[:hostname]) && (options[:hosts] || options[:addresses])
205
+ @logger.warn "The connection options contain both a host and an array of hosts, the single host is ignored."
206
+ end
207
+ end
208
+
196
209
  # @return [String] RabbitMQ hostname (or IP address) used
197
210
  def hostname; self.host; end
198
211
  # @return [String] Username used
@@ -223,11 +236,15 @@ module Bunny
223
236
  end
224
237
 
225
238
  def host
226
- @transport ? @transport.host : @hosts[@host_index]
239
+ @transport ? @transport.host : host_from_address(@addresses[@address_index])
227
240
  end
228
241
 
229
- def reset_host_index
230
- @host_index_mutex.synchronize { @host_index = 0 }
242
+ def port
243
+ @transport ? @transport.port : port_from_address(@addresses[@address_index])
244
+ end
245
+
246
+ def reset_address_index
247
+ @address_index_mutex.synchronize { @address_index = 0 }
231
248
  end
232
249
 
233
250
  # @private
@@ -293,7 +310,7 @@ module Bunny
293
310
  raise
294
311
  end
295
312
  rescue HostListDepleted
296
- self.reset_host_index
313
+ self.reset_address_index
297
314
  @status_mutex.synchronize { @status = :not_connected }
298
315
  raise TCPConnectionFailedForAllHosts
299
316
  end
@@ -659,7 +676,7 @@ module Bunny
659
676
  recover_channels
660
677
  end
661
678
  rescue HostListDepleted
662
- reset_host_index
679
+ reset_address_index
663
680
  retry
664
681
  rescue TCPConnectionFailedForAllHosts, TCPConnectionFailed, AMQ::Protocol::EmptyResponseError => e
665
682
  @logger.warn "TCP connection failed, reconnecting in #{@network_recovery_interval} seconds"
@@ -744,10 +761,23 @@ module Bunny
744
761
  end
745
762
 
746
763
  # @private
747
- def hostnames_from(options)
748
- options.fetch(:hosts_shuffle_strategy, @default_hosts_shuffle_strategy).call(
749
- [ options[:hosts] || options[:host] || options[:hostname] || DEFAULT_HOST ].flatten
750
- )
764
+ def addresses_from(options)
765
+ shuffle_strategy = options.fetch(:hosts_shuffle_strategy, @default_hosts_shuffle_strategy)
766
+
767
+ addresses = options[:host] || options[:hostname] || options[:addresses] ||
768
+ options[:hosts] || ["#{DEFAULT_HOST}:#{port_from(options)}"]
769
+ addresses = [addresses] unless addresses.is_a? Array
770
+
771
+ addresses.map! do |address|
772
+ host_with_port?(address) ? address : "#{address}:#{port_from(@opts)}"
773
+ end
774
+
775
+ shuffle_strategy.call addresses
776
+ end
777
+
778
+ # @private
779
+ def host_with_port?(address)
780
+ address.include? ':'
751
781
  end
752
782
 
753
783
  # @private
@@ -761,6 +791,16 @@ module Bunny
761
791
  options.fetch(:port, fallback)
762
792
  end
763
793
 
794
+ # @private
795
+ def host_from_address(address)
796
+ address.split(":")[0]
797
+ end
798
+
799
+ # @private
800
+ def port_from_address(address)
801
+ address.split(":")[1].to_i
802
+ end
803
+
764
804
  # @private
765
805
  def vhost_from(options)
766
806
  options[:virtual_host] || options[:vhost] || DEFAULT_VHOST
@@ -946,7 +986,7 @@ module Bunny
946
986
  # @api public
947
987
  def to_s
948
988
  oid = ("0x%x" % (self.object_id << 1))
949
- "#<#{self.class.name}:#{oid} #{@user}@#{host}:#{@port}, vhost=#{@vhost}, hosts=[#{@hosts.join(',')}]>"
989
+ "#<#{self.class.name}:#{oid} #{@user}@#{host}:#{port}, vhost=#{@vhost}, addresses=[#{@addresses.join(',')}]>"
950
990
  end
951
991
 
952
992
  def inspect
@@ -1109,10 +1149,14 @@ module Bunny
1109
1149
 
1110
1150
  # @private
1111
1151
  def initialize_transport
1112
- if host = @hosts[ @host_index ]
1113
- @host_index_mutex.synchronize { @host_index += 1 }
1152
+ if address = @addresses[ @address_index ]
1153
+ @address_index_mutex.synchronize { @address_index += 1 }
1114
1154
  @transport.close rescue nil # Let's make sure the previous transport socket is closed
1115
- @transport = Transport.new(self, host, @port, @opts.merge(:session_thread => @origin_thread))
1155
+ @transport = Transport.new(self,
1156
+ host_from_address(address),
1157
+ port_from_address(address),
1158
+ @opts.merge(:session_thread => @origin_thread)
1159
+ )
1116
1160
 
1117
1161
  # Reset the cached progname for the logger only when no logger was provided
1118
1162
  @default_logger.progname = self.to_s
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Bunny
4
4
  # @return [String] Version of the library
5
- VERSION = "2.1.0"
5
+ VERSION = "2.2.0"
6
6
  end
@@ -19,7 +19,6 @@ describe Bunny::Session do
19
19
  end
20
20
  end
21
21
 
22
-
23
22
  it "handles amqp:// URIs w/o path part" do
24
23
  session = described_class.new("amqp://127.0.0.1")
25
24
  session.start
@@ -32,7 +31,6 @@ describe Bunny::Session do
32
31
  session.close
33
32
  end
34
33
 
35
-
36
34
  context "when URI ends in a slash" do
37
35
  it "parses vhost as an empty string" do
38
36
  session = described_class.new("amqp://127.0.0.1/")
@@ -50,9 +48,6 @@ describe Bunny::Session do
50
48
  end
51
49
  end
52
50
 
53
-
54
-
55
-
56
51
  context "initialized with all defaults" do
57
52
  it "provides a way to fine tune socket options" do
58
53
  conn = Bunny.new
@@ -156,7 +151,7 @@ describe Bunny::Session do
156
151
  let(:hosts) { [host] }
157
152
  let(:subject) { described_class.new(:hosts => hosts) }
158
153
 
159
- it "uses hostname = localhost" do
154
+ it "uses hostname = 192.168.1.10" do
160
155
  expect(subject.host).to eq host
161
156
  expect(subject.hostname).to eq host
162
157
  end
@@ -171,6 +166,58 @@ describe Bunny::Session do
171
166
  end
172
167
  end
173
168
 
169
+ context "initialized with :addresses => [...]" do
170
+ after :each do
171
+ subject.close if subject.open?
172
+ end
173
+
174
+ let(:host) { "192.168.1.10" }
175
+ let(:port) { 5673 }
176
+ let(:address) { "#{host}:#{port}" }
177
+ let(:addresses) { [address] }
178
+ let(:subject) { described_class.new(:addresses => addresses) }
179
+
180
+ it "uses hostname = 192.168.1.10" do
181
+ expect(subject.host).to eq host
182
+ expect(subject.hostname).to eq host
183
+ end
184
+
185
+ it "uses port 5673" do
186
+ expect(subject.port).to eq port
187
+ end
188
+
189
+ it "uses username = guest" do
190
+ expect(subject.username).to eq username
191
+ expect(subject.user).to eq username
192
+ end
193
+ end
194
+
195
+ context "initialized with conflicting hosts and addresses" do
196
+ let(:host) { "192.168.1.10" }
197
+ let(:port) { 5673 }
198
+ let(:address) { "#{host}:#{port}" }
199
+ let(:io) { StringIO.new }
200
+ let(:logger) { ::Logger.new(io) }
201
+
202
+ it "raises an argument error when there is are hosts and an address" do
203
+ expect { described_class.new(addresses: [address], hosts: [host]) }.to raise_error(ArgumentError)
204
+ end
205
+
206
+ it "logs a warning when there is a single host and an array" do
207
+ described_class.new(addresses: [address], host: host, logger: logger)
208
+ expect(io.string).to include 'WARN -- : The connection options contain '\
209
+ 'both a host and an array of hosts, the single host is ignored.'
210
+ end
211
+
212
+ it "converts hosts in addresses to addresses" do
213
+ strategy = Proc.new { |addresses| addresses }
214
+ session = described_class.new(addresses: [address,host ], hosts_shuffle_strategy: strategy)
215
+ strategy = Proc.new { |addresses| addresses }
216
+
217
+ expect(session.to_s).to include 'addresses=[192.168.1.10:5673,192.168.1.10:5672]'
218
+ end
219
+ end
220
+
174
221
  context "initialized with :channel_max => 4096" do
175
222
  after :each do
176
223
  subject.close if subject.open?
@@ -223,7 +270,6 @@ describe Bunny::Session do
223
270
  end
224
271
  end
225
272
 
226
-
227
273
  context "initialized with :host => 127.0.0.1 and non-default credentials" do
228
274
  after :each do
229
275
  subject.close if subject.open?
@@ -276,7 +322,6 @@ describe Bunny::Session do
276
322
  end
277
323
  end
278
324
 
279
-
280
325
  context "initialized with :host => 127.0.0.1 and non-default credentials (take 2)" do
281
326
  after :each do
282
327
  subject.close if subject.open?
@@ -324,8 +369,6 @@ describe Bunny::Session do
324
369
  end
325
370
  end
326
371
 
327
-
328
-
329
372
  context "initialized with :host => 127.0.0.1 and non-default credentials (take 2)" do
330
373
  after :each do
331
374
  subject.close if subject.open?
@@ -362,8 +405,6 @@ describe Bunny::Session do
362
405
  end
363
406
  end
364
407
 
365
-
366
-
367
408
  context "initialized with :host => 127.0.0.1 and INVALID credentials" do
368
409
  let(:host) { "127.0.0.1" }
369
410
  # see ./bin/ci/before_build
@@ -390,7 +431,6 @@ describe Bunny::Session do
390
431
  end
391
432
  end
392
433
 
393
-
394
434
  context "initialized with unreachable host or port" do
395
435
  it "fails to connect" do
396
436
  expect do
@@ -422,7 +462,6 @@ describe Bunny::Session do
422
462
  end
423
463
  end
424
464
 
425
-
426
465
  context "initialized with a custom logger object" do
427
466
  let(:io) { StringIO.new }
428
467
  let(:logger) { ::Logger.new(io) }
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: 2.1.0
4
+ version: 2.2.0
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: 2015-08-16 00:00:00.000000000 Z
15
+ date: 2015-09-05 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: amq-protocol
@@ -31,11 +31,7 @@ dependencies:
31
31
  description: Easy to use, feature complete Ruby client for RabbitMQ 3.3 and later
32
32
  versions.
33
33
  email:
34
- - celldee@gmail.com
35
- - eric@5stops.com
36
- - stastny@101ideas.cz
37
- - michael@novemberain.com
38
- - skaes@railsexpress.de
34
+ - michael.s.klishin@gmail.com
39
35
  executables: []
40
36
  extensions: []
41
37
  extra_rdoc_files: