bunny 2.1.0 → 2.2.0

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: 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: