bunny 0.9.0.pre9 → 0.9.0.pre10

Sign up to get free protection for your applications and to get access to all the features.
@@ -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