bunny 0.9.0.pre9 → 0.9.0.pre10

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.
@@ -0,0 +1 @@
1
+ 1.9.3@amqp
@@ -1,5 +1,48 @@
1
+ ## Changes between Bunny 0.9.0.pre9 and 0.9.0.pre10
2
+
3
+ This release contains a **breaking API change**.
4
+
5
+ ### Explicitly Closed Sockets
6
+
7
+ Bunny now will correctly close the socket previous connection had
8
+ when recovering from network issues.
9
+
10
+
11
+ ### Bunny::Exception Now Extends StandardError
12
+
13
+ `Bunny::Exception` now inherits from `StandardError` and not `Exception`.
14
+
15
+ Naked rescue like this
16
+
17
+ ``` ruby
18
+ begin
19
+ # ...
20
+ rescue => e
21
+ # ...
22
+ end
23
+ ```
24
+
25
+ catches only descendents of `StandardError`. Most people don't
26
+ know this and this is a very counter-intuitive practice, but
27
+ apparently there is code out there that can't be changed that
28
+ depends on this behavior.
29
+
30
+ This is a **breaking API change**.
31
+
32
+
33
+
1
34
  ## Changes between Bunny 0.9.0.pre8 and 0.9.0.pre9
2
35
 
36
+ ### Bunny::Session#start Now Returns a Session
37
+
38
+ `Bunny::Session#start` now returns a session instead of the default channel
39
+ (which wasn't intentional, default channel is a backwards-compatibility implementation
40
+ detail).
41
+
42
+ `Bunny::Session#start` also no longer leaves dead threads behind if called multiple
43
+ times on the same connection.
44
+
45
+
3
46
  ### More Reliable Heartbeat Sender
4
47
 
5
48
  Heartbeat sender no longer slips into an infinite loop if it encounters an exception.
data/Gemfile CHANGED
@@ -21,7 +21,7 @@ extend Module.new {
21
21
 
22
22
  gem "SystemTimer", "1.2", :platform => :ruby_18
23
23
 
24
- gem "rake"
24
+ gem "rake", ">= 10.0.4"
25
25
  gem "effin_utf8"
26
26
 
27
27
  group :development do
@@ -30,7 +30,7 @@ group :development do
30
30
  end
31
31
 
32
32
  group :test do
33
- gem "rspec", "~> 2.8.0"
33
+ gem "rspec", ">= 2.13.0"
34
34
  end
35
35
 
36
36
  gemspec
data/README.md CHANGED
@@ -7,7 +7,7 @@ features and is free of many limitations of earlier versions.
7
7
 
8
8
  ## Supported Ruby Versions
9
9
 
10
- Bunny 0.9 and more recent versions support Ruby 1.9.3, 1.9.2, JRuby 1.7, Rubinius 2.0 and 1.8.7.
10
+ Bunny 0.9 and more recent versions support Ruby 1.9.3, 1.9.2, 2.0, JRuby 1.7, Rubinius 2.0 and 1.8.7.
11
11
 
12
12
 
13
13
  ## Supported RabbitMQ Versions
@@ -33,10 +33,12 @@ To install Bunny 0.9.x with RubyGems:
33
33
  gem install bunny --pre
34
34
  ```
35
35
 
36
+ the most recent 0.9.x version is `0.9.0.pre10`.
37
+
36
38
  To use Bunny 0.9.x in a project managed with Bundler:
37
39
 
38
40
  ``` ruby
39
- gem "bunny", ">= 0.9.0.pre8" # optionally: , :git => "git://github.com/ruby-amqp/bunny.git", :branch => "master"
41
+ gem "bunny", ">= 0.9.0.pre10" # optionally: , :git => "git://github.com/ruby-amqp/bunny.git", :branch => "master"
40
42
  ```
41
43
 
42
44
 
@@ -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.2.0"
32
+ s.add_dependency "amq-protocol", ">= 1.4.0"
33
33
 
34
34
  # Files.
35
35
  s.has_rdoc = true
@@ -11,6 +11,12 @@ require "bunny/delivery_info"
11
11
  require "bunny/return_info"
12
12
  require "bunny/message_properties"
13
13
 
14
+ if defined?(JRUBY_VERSION)
15
+ require "bunny/concurrent/linked_continuation_queue"
16
+ else
17
+ require "bunny/concurrent/continuation_queue"
18
+ end
19
+
14
20
  module Bunny
15
21
  # ## What are AMQP channels
16
22
  #
@@ -169,9 +175,8 @@ module Bunny
169
175
 
170
176
  @unconfirmed_set_mutex = Mutex.new
171
177
 
172
- @continuations = ::Queue.new
173
- @confirms_continuations = ::Queue.new
174
- @basic_get_continuations = ::Queue.new
178
+ self.reset_continuations
179
+
175
180
  # threads awaiting on continuations. Used to unblock
176
181
  # them when network connection goes down so that busy loops
177
182
  # that perform synchronous operations can work. MK.
@@ -1735,5 +1740,25 @@ module Bunny
1735
1740
  def raise_if_no_longer_open!
1736
1741
  raise ChannelAlreadyClosed.new("cannot use a channel that was already closed! Channel id: #{@id}", self) if closed?
1737
1742
  end
1738
- end
1739
- end
1743
+
1744
+ # @api private
1745
+ def reset_continuations
1746
+ @continuations = new_continuation
1747
+ @confirms_continuations = new_continuation
1748
+ @basic_get_continuations = new_continuation
1749
+ end
1750
+
1751
+
1752
+ if defined?(JRUBY_VERSION)
1753
+ # @api private
1754
+ def new_continuation
1755
+ Concurrent::LinkedContinuationQueue.new
1756
+ end
1757
+ else
1758
+ # @api private
1759
+ def new_continuation
1760
+ Concurrent::ContinuationQueue.new
1761
+ end
1762
+ end # if defined?
1763
+ end # Channel
1764
+ end # Bunny
@@ -1,5 +1,5 @@
1
1
  module Bunny
2
- class Exception < ::Exception
2
+ class Exception < ::StandardError
3
3
  end
4
4
 
5
5
  class NetworkFailure < Exception
@@ -171,15 +171,23 @@ module Bunny
171
171
  @status = :connecting
172
172
  self.reset_continuations
173
173
 
174
- self.initialize_transport
174
+ begin
175
+ # close existing transport if we have one,
176
+ # to not leak sockets
177
+ self.maybe_close_transport
178
+ self.initialize_transport
175
179
 
176
- self.init_connection
177
- self.open_connection
180
+ self.init_connection
181
+ self.open_connection
178
182
 
179
- @event_loop = nil
180
- self.start_main_loop if @threaded
183
+ @event_loop = nil
184
+ self.start_main_loop if @threaded
181
185
 
182
- @default_channel = self.create_channel
186
+ @default_channel = self.create_channel
187
+ rescue Exception => e
188
+ @status = :not_connected
189
+ raise e
190
+ end
183
191
 
184
192
  self
185
193
  end
@@ -733,6 +741,11 @@ module Bunny
733
741
  @transport = Transport.new(self, @host, @port, @opts.merge(:session_thread => Thread.current))
734
742
  end
735
743
 
744
+ # @api private
745
+ def maybe_close_transport
746
+ @transport.close if @transport
747
+ end
748
+
736
749
  # Sends AMQ protocol header (also known as preamble).
737
750
  # @api private
738
751
  def send_preamble
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Bunny
4
- VERSION = "0.9.0.pre9"
4
+ VERSION = "0.9.0.pre10"
5
5
  end
@@ -338,4 +338,37 @@ describe Bunny::Session do
338
338
  subject.password.should == password
339
339
  end
340
340
  end
341
+
342
+
343
+ context "initialized with a disconnected host" do
344
+ subject do
345
+ described_class.new(:port => 38000)
346
+ end
347
+
348
+ it "fails to connect" do
349
+ lambda do
350
+ subject.start
351
+ end.should raise_error(Bunny::TCPConnectionFailed)
352
+ end
353
+
354
+ it "is not connected" do
355
+ begin
356
+ subject.start
357
+ rescue Bunny::TCPConnectionFailed => e
358
+ true
359
+ end
360
+
361
+ subject.status.should == :not_connected
362
+ end
363
+
364
+ it "is not open" do
365
+ begin
366
+ subject.start
367
+ rescue Bunny::TCPConnectionFailed => e
368
+ true
369
+ end
370
+
371
+ subject.should_not be_open
372
+ end
373
+ end
341
374
  end
@@ -1,48 +1,50 @@
1
1
  require "spec_helper"
2
2
 
3
- describe "Rapidly opening and closing lots of channels" do
4
- let(:connection) do
5
- c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :automatic_recovery => false)
6
- c.start
7
- c
8
- end
3
+ unless ENV["CI"]
4
+ describe "Rapidly opening and closing lots of channels" do
5
+ let(:connection) do
6
+ c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :automatic_recovery => false)
7
+ c.start
8
+ c
9
+ end
9
10
 
10
- after :all do
11
- connection.close
12
- end
11
+ after :all do
12
+ connection.close
13
+ end
13
14
 
14
- context "in a single-threaded scenario" do
15
- let(:n) { 500 }
15
+ context "in a single-threaded scenario" do
16
+ let(:n) { 500 }
16
17
 
17
- it "works correctly" do
18
- xs = Array.new(n) { connection.create_channel }
19
- puts "Opened #{n} channels"
18
+ it "works correctly" do
19
+ xs = Array.new(n) { connection.create_channel }
20
+ puts "Opened #{n} channels"
20
21
 
21
- xs.size.should == n
22
- xs.each do |ch|
23
- ch.close
22
+ xs.size.should == n
23
+ xs.each do |ch|
24
+ ch.close
25
+ end
24
26
  end
25
27
  end
26
- end
27
28
 
28
- context "in a multi-threaded scenario" do
29
- # actually, on MRI values greater than ~100 will eventually cause write
30
- # operations to fail with a timeout (1 second is not enough)
31
- # which will cause recovery to re-acquire @channel_mutex in Session.
32
- # Because Ruby's mutexes are not re-entrant, it will raise a ThreadError.
33
- #
34
- # But this already demonstrates that within these platform constraints,
35
- # Bunny is safe to use in such scenarios.
36
- let(:n) { 50 }
37
-
38
- it "works correctly" do
39
- n.times do
40
- t = Thread.new do
41
- ch = connection.create_channel
42
-
43
- ch.close
29
+ context "in a multi-threaded scenario" do
30
+ # actually, on MRI values greater than ~100 will eventually cause write
31
+ # operations to fail with a timeout (1 second is not enough)
32
+ # which will cause recovery to re-acquire @channel_mutex in Session.
33
+ # Because Ruby's mutexes are not re-entrant, it will raise a ThreadError.
34
+ #
35
+ # But this already demonstrates that within these platform constraints,
36
+ # Bunny is safe to use in such scenarios.
37
+ let(:n) { 20 }
38
+
39
+ it "works correctly" do
40
+ n.times do
41
+ t = Thread.new do
42
+ ch = connection.create_channel
43
+
44
+ ch.close
45
+ end
46
+ t.abort_on_exception = true
44
47
  end
45
- t.abort_on_exception = true
46
48
  end
47
49
  end
48
50
  end
@@ -1,48 +1,49 @@
1
1
  require "spec_helper"
2
2
 
3
- describe "Rapidly opening and closing lots of channels on a non-threaded connection" do
4
- let(:connection) do
5
- c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :automatic_recovery => false, :threaded => false)
6
- c.start
7
- c
8
- end
3
+ unless ENV["CI"]
4
+ describe "Rapidly opening and closing lots of channels on a non-threaded connection" do
5
+ let(:connection) do
6
+ c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :automatic_recovery => false, :threaded => false)
7
+ c.start
8
+ c
9
+ end
9
10
 
10
- after :all do
11
- connection.close
12
- end
11
+ after :all do
12
+ connection.close
13
+ end
13
14
 
14
- context "in a single-threaded scenario" do
15
- let(:n) { 500 }
15
+ context "in a single-threaded scenario" do
16
+ let(:n) { 500 }
16
17
 
17
- it "works correctly" do
18
- xs = Array.new(n) { connection.create_channel }
19
- puts "Opened #{n} channels"
18
+ it "works correctly" do
19
+ xs = Array.new(n) { connection.create_channel }
20
20
 
21
- xs.size.should == n
22
- xs.each do |ch|
23
- ch.close
21
+ xs.size.should == n
22
+ xs.each do |ch|
23
+ ch.close
24
+ end
24
25
  end
25
26
  end
26
- end
27
27
 
28
- context "in a multi-threaded scenario" do
29
- # actually, on MRI values greater than ~100 will eventually cause write
30
- # operations to fail with a timeout (1 second is not enough)
31
- # which will cause recovery to re-acquire @channel_mutex in Session.
32
- # Because Ruby's mutexes are not re-entrant, it will raise a ThreadError.
33
- #
34
- # But this already demonstrates that within these platform constraints,
35
- # Bunny is safe to use in such scenarios.
36
- let(:n) { 50 }
37
-
38
- it "works correctly" do
39
- n.times do
40
- t = Thread.new do
41
- ch = connection.create_channel
42
-
43
- ch.close
28
+ context "in a multi-threaded scenario" do
29
+ # actually, on MRI values greater than ~100 will eventually cause write
30
+ # operations to fail with a timeout (1 second is not enough)
31
+ # which will cause recovery to re-acquire @channel_mutex in Session.
32
+ # Because Ruby's mutexes are not re-entrant, it will raise a ThreadError.
33
+ #
34
+ # But this already demonstrates that within these platform constraints,
35
+ # Bunny is safe to use in such scenarios.
36
+ let(:n) { 20 }
37
+
38
+ it "works correctly" do
39
+ n.times do
40
+ t = Thread.new do
41
+ ch = connection.create_channel
42
+
43
+ ch.close
44
+ end
45
+ t.abort_on_exception = true
44
46
  end
45
- t.abort_on_exception = true
46
47
  end
47
48
  end
48
49
  end
@@ -1,66 +1,68 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  require "spec_helper"
3
3
 
4
- describe "Concurrent consumers sharing a connection" do
5
- let(:connection) do
6
- c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :automatic_recovery => false)
7
- c.start
8
- c
9
- end
4
+ unless ENV["CI"]
5
+ describe "Concurrent consumers sharing a connection" do
6
+ let(:connection) do
7
+ c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :automatic_recovery => false)
8
+ c.start
9
+ c
10
+ end
10
11
 
11
- after :all do
12
- connection.close
13
- end
12
+ after :all do
13
+ connection.close
14
+ end
14
15
 
15
- def any_not_drained?(qs)
16
- qs.any? { |q| !q.message_count.zero? }
17
- end
16
+ def any_not_drained?(qs)
17
+ qs.any? { |q| !q.message_count.zero? }
18
+ end
18
19
 
19
- context "when publishing thousands of messages over 128K in size" do
20
- let(:colors) { ["red", "blue", "white"] }
20
+ context "when publishing thousands of messages over 128K in size" do
21
+ let(:colors) { ["red", "blue", "white"] }
21
22
 
22
- let(:n) { 32 }
23
- let(:m) { 1000 }
23
+ let(:n) { 32 }
24
+ let(:m) { 1000 }
24
25
 
25
- it "successfully drain all queues" do
26
- ch = connection.create_channel
27
- body = "абвг"
28
- x = ch.topic("bunny.stress.concurrent.consumers.topic", :durable => true)
26
+ it "successfully drain all queues" do
27
+ ch = connection.create_channel
28
+ body = "абвг"
29
+ x = ch.topic("bunny.stress.concurrent.consumers.topic", :durable => true)
29
30
 
30
- chs = {}
31
- n.times do |i|
32
- chs[i] = connection.create_channel
33
- end
34
- qs = []
31
+ chs = {}
32
+ n.times do |i|
33
+ chs[i] = connection.create_channel
34
+ end
35
+ qs = []
35
36
 
36
- n.times do |i|
37
- t = Thread.new do
38
- cht = chs[i]
37
+ n.times do |i|
38
+ t = Thread.new do
39
+ cht = chs[i]
39
40
 
40
- q = cht.queue("", :exclusive => true)
41
- q.bind(x.name, :routing_key => colors.sample).subscribe do |delivery_info, meta, payload|
42
- # no-op
41
+ q = cht.queue("", :exclusive => true)
42
+ q.bind(x.name, :routing_key => colors.sample).subscribe do |delivery_info, meta, payload|
43
+ # no-op
44
+ end
45
+ qs << q
43
46
  end
44
- qs << q
47
+ t.abort_on_exception = true
45
48
  end
46
- t.abort_on_exception = true
47
- end
48
49
 
49
- sleep 1.0
50
+ sleep 1.0
50
51
 
51
- 5.times do |i|
52
- m.times do
53
- x.publish(body, :routing_key => colors.sample)
52
+ 5.times do |i|
53
+ m.times do
54
+ x.publish(body, :routing_key => colors.sample)
55
+ end
56
+ puts "Published #{(i + 1) * m} messages..."
54
57
  end
55
- puts "Published #{(i + 1) * m} messages..."
56
- end
57
58
 
58
- while any_not_drained?(qs)
59
- sleep 1.0
60
- end
61
- puts "Drained all the queues..."
59
+ while any_not_drained?(qs)
60
+ sleep 1.0
61
+ end
62
+ puts "Drained all the queues..."
62
63
 
63
- ch.close
64
+ ch.close
65
+ end
64
66
  end
65
67
  end
66
68
  end
@@ -1,58 +1,60 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  require "spec_helper"
3
3
 
4
- describe "Concurrent publishers sharing a connection" do
5
- let(:connection) do
6
- c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :automatically_recover => false)
7
- c.start
8
- c
9
- end
4
+ unless ENV["CI"]
5
+ describe "Concurrent publishers sharing a connection" do
6
+ let(:connection) do
7
+ c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :automatically_recover => false)
8
+ c.start
9
+ c
10
+ end
10
11
 
11
- after :all do
12
- connection.close
13
- end
12
+ after :all do
13
+ connection.close
14
+ end
14
15
 
15
- let(:n) { 32 }
16
- let(:m) { 1000 }
16
+ let(:n) { 32 }
17
+ let(:m) { 1000 }
17
18
 
18
- it "successfully finish publishing" do
19
- ch = connection.create_channel
19
+ it "successfully finish publishing" do
20
+ ch = connection.create_channel
20
21
 
21
- q = ch.queue("", :exclusive => true)
22
- body = "сообщение"
22
+ q = ch.queue("", :exclusive => true)
23
+ body = "сообщение"
23
24
 
24
- # let the queue name be sent back by RabbitMQ
25
- sleep 0.25
25
+ # let the queue name be sent back by RabbitMQ
26
+ sleep 0.25
26
27
 
27
- chs = {}
28
- n.times do |i|
29
- chs[i] = connection.create_channel
30
- end
28
+ chs = {}
29
+ n.times do |i|
30
+ chs[i] = connection.create_channel
31
+ end
31
32
 
32
- ts = []
33
+ ts = []
33
34
 
34
- n.times do |i|
35
- t = Thread.new do
36
- cht = chs[i]
37
- x = ch.default_exchange
35
+ n.times do |i|
36
+ t = Thread.new do
37
+ cht = chs[i]
38
+ x = ch.default_exchange
38
39
 
39
- 5.times do |i|
40
- m.times do
41
- x.publish(body, :routing_key => q.name)
40
+ 5.times do |i|
41
+ m.times do
42
+ x.publish(body, :routing_key => q.name)
43
+ end
44
+ puts "Published #{(i + 1) * m} messages..."
42
45
  end
43
- puts "Published #{(i + 1) * m} messages..."
44
46
  end
47
+ t.abort_on_exception = true
48
+
49
+ ts << t
45
50
  end
46
- t.abort_on_exception = true
47
51
 
48
- ts << t
49
- end
52
+ ts.each do |t|
53
+ t.join
54
+ end
50
55
 
51
- ts.each do |t|
52
- t.join
56
+ sleep 4.0
57
+ q.message_count.should == 5 * n * m
53
58
  end
54
-
55
- sleep 4.0
56
- q.message_count.should == 5 * n * m
57
59
  end
58
60
  end
@@ -45,27 +45,29 @@ describe Bunny::Concurrent::Condition do
45
45
  end
46
46
 
47
47
  describe "#notify_all" do
48
+ let(:n) { 20 }
49
+
48
50
  it "notifies all the threads waiting on the latch" do
49
51
  condition = described_class.new
50
52
  @xs = []
51
53
 
52
- t1 = Thread.new do
53
- condition.wait
54
- @xs << :notified1
55
- end
56
- t1.abort_on_exception = true
57
-
58
- t2 = Thread.new do
59
- condition.wait
60
- @xs << :notified2
54
+ n.times do |i|
55
+ t = Thread.new do
56
+ condition.wait
57
+ @xs << "notified#{i + 1}".to_sym
58
+ end
59
+ t.abort_on_exception = true
61
60
  end
62
- t2.abort_on_exception = true
63
61
 
64
62
  sleep 0.5
65
63
  condition.notify_all
66
64
  sleep 0.5
67
- @xs.should include(:notified1)
68
- @xs.should include(:notified2)
65
+
66
+ n.times do |i|
67
+ item = "notified#{i + 1}".to_sym
68
+
69
+ @xs.should include(item)
70
+ end
69
71
  end
70
72
  end
71
73
  end
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: 0.9.0.pre9
4
+ version: 0.9.0.pre10
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2013-04-22 00:00:00.000000000 Z
16
+ date: 2013-05-02 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: amq-protocol
@@ -21,13 +21,13 @@ dependencies:
21
21
  requirements:
22
22
  - - ">="
23
23
  - !ruby/object:Gem::Version
24
- version: 1.2.0
24
+ version: 1.4.0
25
25
  none: false
26
26
  requirement: !ruby/object:Gem::Requirement
27
27
  requirements:
28
28
  - - ">="
29
29
  - !ruby/object:Gem::Version
30
- version: 1.2.0
30
+ version: 1.4.0
31
31
  none: false
32
32
  prerelease: false
33
33
  type: :runtime
@@ -50,6 +50,7 @@ extra_rdoc_files:
50
50
  files:
51
51
  - ".gitignore"
52
52
  - ".rspec"
53
+ - ".ruby-version"
53
54
  - ".travis.yml"
54
55
  - ".yardopts"
55
56
  - ChangeLog.md