bunny 1.3.1 → 1.4.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: d9ff439246dbe426e92dd28209ffa0acab8fb5e9
4
- data.tar.gz: 3762043ad87100abe20a84a0c63b5c4f0e54142a
3
+ metadata.gz: f1cf8263f0250d0bf8f2816bf1aaad837c5505eb
4
+ data.tar.gz: bafa849e30666c22ff3574507567d27d7859f89b
5
5
  SHA512:
6
- metadata.gz: 766ba0c503ae3df0969214dee0d9d00da5550514aee46d3b5a95c232b78b62df0099f601ea2c85154ff0dce261ce41fc35ab4a2d91bf24aa4110cbb61176bb94
7
- data.tar.gz: d5f8c93ac81cb252717c2e5d98fe06e4ab5e5027a82329f0ce2622f5b0e398b0f1b297af3f7f0ade43a548ff6e0724b8a6f75249ea38c9cd5300e3d8f8231499
6
+ metadata.gz: a5e14b69c7a128c20e95e2850e80d5517f27f1d93068468ff0058cf9c041a1a8f243ec9c67119580e55dcedb9c6370d59eea15a38be2384b38678fee4307c591
7
+ data.tar.gz: 849e7085e43ef4b3859940f492472c5058ffffdd0d18d1cdb1946fcf39b7b7761d7f2b4150f05dac6c7b39aa7e2a0e295d8a267d397d3b74815e9b97b4ab881f
@@ -1,8 +1,20 @@
1
- ## Changes between Bunny 1.3.0 and 1.3.1
1
+ ## Changes between Bunny 1.3.0 and 1.4.0
2
+
3
+ ### Channel#wait_for_confirms Returns Immediately If All Publishes Confirmed
4
+
5
+ Contributed by Matt Campbell.
6
+
7
+ ### Publisher Confirms is In Sync After Recovery
8
+
9
+ When a connection is recovered, the sequence counter resets on the
10
+ broker, but not the client. To keep things in sync the client must store a confirmation
11
+ offset after a recovery.
12
+
13
+ Contributed by Devin Christensen.
2
14
 
3
15
  ### NoMethodError on Thread During Shutdown
4
16
 
5
- During abnormal termination, `Bunny::Session` no longer tries
17
+ During abnormal termination, `Bunny::Session#close` no longer tries
6
18
  to call the non-existent `terminate_with` method on its origin
7
19
  thread.
8
20
 
data/Gemfile CHANGED
@@ -35,7 +35,7 @@ end
35
35
 
36
36
  group :test do
37
37
  gem "rspec", "~> 2.13.0"
38
- gem "rabbitmq_http_api_client", "~> 1.1.0"
38
+ gem "rabbitmq_http_api_client", "~> 1.2.0"
39
39
  end
40
40
 
41
41
  gemspec
data/README.md CHANGED
@@ -95,7 +95,7 @@ gem install bunny
95
95
  To use Bunny in a project managed with Bundler:
96
96
 
97
97
  ``` ruby
98
- gem "bunny", "~> 1.1.7"
98
+ gem "bunny", "~> 1.3.1"
99
99
  ```
100
100
 
101
101
 
@@ -162,7 +162,7 @@ Other documentation guides are available at [rubybunny.info](http://rubybunny.in
162
162
  ### Mailing List
163
163
 
164
164
  [Bunny has a mailing list](http://groups.google.com/group/ruby-amqp). We encourage you
165
- to also join the [rabbitmq-discuss](https://lists.rabbitmq.com/cgi-bin/mailman/listinfo/rabbitmq-discuss) mailing list. Feel free to ask any questions that you may have.
165
+ to also join the [RabbitMQ mailing list](https://groups.google.com/forum/#!forum/rabbitmq-users) mailing list. Feel free to ask any questions that you may have.
166
166
 
167
167
 
168
168
  ### IRC
@@ -195,6 +195,7 @@ module Bunny
195
195
  @threads_waiting_on_basic_get_continuations = Set.new
196
196
 
197
197
  @next_publish_seq_no = 0
198
+ @delivery_tag_offset = 0
198
199
 
199
200
  @recoveries_counter = Bunny::Concurrent::AtomicFixnum.new(0)
200
201
  @uncaught_exception_handler = Proc.new do |e, consumer|
@@ -1359,6 +1360,7 @@ module Bunny
1359
1360
  @unconfirmed_set = Set.new
1360
1361
  @nacked_set = Set.new
1361
1362
  @next_publish_seq_no = 1
1363
+ @only_acks_received = true
1362
1364
  end
1363
1365
 
1364
1366
  @confirms_callback = callback
@@ -1373,19 +1375,18 @@ module Bunny
1373
1375
  end
1374
1376
 
1375
1377
  # Blocks calling thread until confirms are received for all
1376
- # currently unacknowledged published messages.
1378
+ # currently unacknowledged published messages. Returns immediately
1379
+ # if there are no outstanding confirms.
1377
1380
  #
1378
- # @return [Boolean] true if all messages were acknowledged positively, false otherwise
1381
+ # @return [Boolean] true if all messages were acknowledged positively since the last time this method was called, false otherwise
1379
1382
  # @see #confirm_select
1380
1383
  # @see #unconfirmed_set
1381
1384
  # @see #nacked_set
1382
1385
  # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
1383
1386
  # @api public
1384
1387
  def wait_for_confirms
1385
- @only_acks_received = true
1386
1388
  wait_on_confirms_continuations
1387
-
1388
- @only_acks_received
1389
+ read_and_reset_only_acks_received
1389
1390
  end
1390
1391
 
1391
1392
  # @endgroup
@@ -1467,7 +1468,10 @@ module Bunny
1467
1468
  #
1468
1469
  # @api plugin
1469
1470
  def recover_confirm_mode
1470
- confirm_select if @confirm_mode
1471
+ if using_publisher_confirmations?
1472
+ @delivery_tag_offset = @next_publish_seq_no - 1
1473
+ confirm_select(@confirms_callback)
1474
+ end
1471
1475
  end
1472
1476
 
1473
1477
  # Recovers transaction mode. Used by the Automatic Network Failure
@@ -1691,28 +1695,26 @@ module Bunny
1691
1695
  end
1692
1696
 
1693
1697
  # @private
1694
- def handle_ack_or_nack(delivery_tag, multiple, nack)
1698
+ def handle_ack_or_nack(delivery_tag_before_offset, multiple, nack)
1699
+ delivery_tag = delivery_tag_before_offset + @delivery_tag_offset
1700
+ confirmed_range_start = multiple ? @delivery_tag_offset + 1 : delivery_tag
1701
+ confirmed_range_end = delivery_tag
1702
+ confirmed_range = (confirmed_range_start..confirmed_range_end)
1703
+
1695
1704
  @unconfirmed_set_mutex.synchronize do
1696
1705
  if nack
1697
- cloned_set = @unconfirmed_set.clone
1698
- if multiple
1699
- cloned_set.keep_if { |i| i <= delivery_tag }
1700
- @nacked_set.merge(cloned_set)
1701
- else
1702
- @nacked_set.add(delivery_tag)
1703
- end
1706
+ @nacked_set.merge(@unconfirmed_set & confirmed_range)
1704
1707
  end
1705
1708
 
1706
- if multiple
1707
- @unconfirmed_set.delete_if { |i| i <= delivery_tag }
1708
- else
1709
- @unconfirmed_set.delete(delivery_tag)
1710
- end
1709
+ @unconfirmed_set.subtract(confirmed_range)
1711
1710
 
1712
1711
  @only_acks_received = (@only_acks_received && !nack)
1713
1712
 
1714
1713
  @confirms_continuations.push(true) if @unconfirmed_set.empty?
1715
- @confirms_callback.call(delivery_tag, multiple, nack) if @confirms_callback
1714
+
1715
+ if @confirms_callback
1716
+ confirmed_range.each { |tag| @confirms_callback.call(tag, false, nack) }
1717
+ end
1716
1718
  end
1717
1719
  end
1718
1720
 
@@ -1759,17 +1761,32 @@ module Bunny
1759
1761
  @threads_waiting_on_confirms_continuations << t
1760
1762
 
1761
1763
  begin
1762
- @confirms_continuations.poll(@connection.continuation_timeout)
1764
+ outstanding_confirms = false
1765
+ @unconfirmed_set_mutex.synchronize do
1766
+ outstanding_confirms = !@unconfirmed_set.empty?
1767
+ end
1768
+ @confirms_continuations.poll(@connection.continuation_timeout) if outstanding_confirms
1763
1769
  ensure
1764
1770
  @threads_waiting_on_confirms_continuations.delete(t)
1765
1771
  end
1766
1772
  else
1767
- connection.reader_loop.run_once until @confirms_continuations.length > 0
1773
+ unless @unconfirmed_set.empty?
1774
+ connection.reader_loop.run_once until @confirms_continuations.length > 0
1775
+ @confirms_continuations.pop
1776
+ end
1777
+ end
1778
+ end
1768
1779
 
1769
- @confirms_continuations.pop
1780
+ # @private
1781
+ def read_and_reset_only_acks_received
1782
+ @unconfirmed_set_mutex.synchronize do
1783
+ result = @only_acks_received
1784
+ @only_acks_received = true
1785
+ result
1770
1786
  end
1771
1787
  end
1772
1788
 
1789
+
1773
1790
  # Releases all continuations. Used by automatic network recovery.
1774
1791
  # @private
1775
1792
  def release_all_continuations
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Bunny
4
4
  # @return [String] Version of the library
5
- VERSION = "1.3.1"
5
+ VERSION = "1.4.0"
6
6
  end
@@ -1,53 +1,13 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Bunny::Channel do
4
- let(:connection) do
5
- c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :continuation_timeout => 10000)
6
- c.start
7
- c
8
- end
9
-
10
4
  after :each do
11
5
  connection.close if connection.open?
12
6
  end
13
7
 
14
8
  let(:n) { 200 }
15
9
 
16
- context "when publishing with confirms enabled" do
17
- it "increments delivery index" do
18
- ch = connection.create_channel
19
- ch.should_not be_using_publisher_confirmations
20
-
21
- ch.confirm_select
22
- ch.should be_using_publisher_confirmations
23
-
24
- q = ch.queue("", :exclusive => true)
25
- x = ch.default_exchange
26
-
27
- n.times do
28
- x.publish("xyzzy", :routing_key => q.name)
29
- end
30
-
31
- ch.next_publish_seq_no.should == n + 1
32
- ch.wait_for_confirms.should be_true
33
- sleep 0.25
34
-
35
- q.message_count.should == n
36
- q.purge
37
-
38
- ch.close
39
- end
40
- end
41
-
42
-
43
- context "with a single-threaded connection" do
44
- let(:connection) do
45
- c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :continuation_timeout => 10000, :threaded => false)
46
- c.start
47
- c
48
- end
49
-
50
-
10
+ shared_examples "publish confirms" do
51
11
  context "when publishing with confirms enabled" do
52
12
  it "increments delivery index" do
53
13
  ch = connection.create_channel
@@ -72,6 +32,95 @@ describe Bunny::Channel do
72
32
 
73
33
  ch.close
74
34
  end
35
+
36
+ describe "#wait_for_confirms" do
37
+ it "should not hang when all the publishes are confirmed" do
38
+ ch = connection.create_channel
39
+ ch.should_not be_using_publisher_confirmations
40
+
41
+ ch.confirm_select
42
+ ch.should be_using_publisher_confirmations
43
+
44
+ q = ch.queue("", :exclusive => true)
45
+ x = ch.default_exchange
46
+
47
+ n.times do
48
+ x.publish("xyzzy", :routing_key => q.name)
49
+ end
50
+
51
+ ch.next_publish_seq_no.should == n + 1
52
+ ch.wait_for_confirms.should be_true
53
+
54
+ sleep 0.25
55
+
56
+ expect {
57
+ Bunny::Timeout.timeout(2) do
58
+ ch.wait_for_confirms.should be_true
59
+ end
60
+ }.not_to raise_error
61
+
62
+ end
63
+ end
64
+
65
+ context "when some of the messages get nacked" do
66
+ it "puts the nacks in the nacked_set" do
67
+ ch = connection.create_channel
68
+ ch.should_not be_using_publisher_confirmations
69
+
70
+ ch.confirm_select
71
+ ch.should be_using_publisher_confirmations
72
+
73
+ q = ch.queue("", :exclusive => true)
74
+ x = ch.default_exchange
75
+
76
+ n.times do
77
+ x.publish("xyzzy", :routing_key => q.name)
78
+ end
79
+
80
+ #be sneaky to simulate a nack
81
+ nacked_tag = nil
82
+ ch.instance_variable_get(:@unconfirmed_set_mutex).synchronize do
83
+ expect(ch.unconfirmed_set).to_not be_empty
84
+ nacked_tag = ch.unconfirmed_set.reduce(ch.next_publish_seq_no - 1) { |lowest, i| i < lowest ? i : lowest }
85
+ ch.handle_ack_or_nack(nacked_tag, false, true)
86
+ end
87
+
88
+ ch.nacked_set.should_not be_empty
89
+ ch.nacked_set.should include(nacked_tag)
90
+
91
+ ch.next_publish_seq_no.should == n + 1
92
+ ch.wait_for_confirms.should be_false
93
+
94
+ ch.nacked_set.should_not be_empty
95
+ ch.nacked_set.should include(nacked_tag)
96
+
97
+ sleep 0.25
98
+ q.message_count.should == n
99
+ q.purge
100
+
101
+ ch.close
102
+ end
103
+ end
75
104
  end
76
105
  end
106
+
107
+ context "with a multi-threaded connection" do
108
+ let(:connection) do
109
+ c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :continuation_timeout => 10000)
110
+ c.start
111
+ c
112
+ end
113
+
114
+ include_examples "publish confirms"
115
+ end
116
+
117
+ context "with a single-threaded connection" do
118
+ let(:connection) do
119
+ c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :continuation_timeout => 10000, :threaded => false)
120
+ c.start
121
+ c
122
+ end
123
+
124
+ include_examples "publish confirms"
125
+ end
77
126
  end
@@ -0,0 +1,40 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "spec_helper"
3
+
4
+ unless ENV["CI"]
5
+ describe "Message framing implementation" do
6
+ let(:connection) do
7
+ c = Bunny.new(:user => "bunny_gem",
8
+ :password => "bunny_password",
9
+ :vhost => "bunny_testbed",
10
+ :port => ENV.fetch("RABBITMQ_PORT", 5672))
11
+ c.start
12
+ c
13
+ end
14
+
15
+ after :each do
16
+ connection.close if connection.open?
17
+ end
18
+
19
+
20
+ context "with payload 272179 bytes in size" do
21
+ it "successfully frames the message" do
22
+ ch = connection.create_channel
23
+
24
+ q = ch.queue("", :exclusive => true)
25
+ x = ch.default_exchange
26
+
27
+ as = ("a" * 272179)
28
+ x.publish(as, :routing_key => q.name, :persistent => true)
29
+
30
+ sleep(1)
31
+ q.message_count.should == 1
32
+
33
+ _, _, payload = q.pop
34
+ payload.bytesize.should == as.bytesize
35
+
36
+ ch.close
37
+ end
38
+ end
39
+ end
40
+ 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: 1.3.1
4
+ version: 1.4.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: 2014-06-19 00:00:00.000000000 Z
15
+ date: 2014-07-29 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: amq-protocol
@@ -184,6 +184,7 @@ files:
184
184
  - spec/issues/issue100_spec.rb
185
185
  - spec/issues/issue141_spec.rb
186
186
  - spec/issues/issue202_spec.rb
187
+ - spec/issues/issue224_spec.rb
187
188
  - spec/issues/issue78_spec.rb
188
189
  - spec/issues/issue83_spec.rb
189
190
  - spec/issues/issue97_attachment.json
@@ -281,6 +282,7 @@ test_files:
281
282
  - spec/issues/issue100_spec.rb
282
283
  - spec/issues/issue141_spec.rb
283
284
  - spec/issues/issue202_spec.rb
285
+ - spec/issues/issue224_spec.rb
284
286
  - spec/issues/issue78_spec.rb
285
287
  - spec/issues/issue83_spec.rb
286
288
  - spec/issues/issue97_attachment.json