bunny 1.3.1 → 1.4.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: 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