bunny 2.0.0.rc1 → 2.0.0.rc2

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: 7035cf94d8295c49d7e57deb2143be7ed1b6a465
4
- data.tar.gz: 0387323f9a4029809fbe05c5160731c2a9839ccc
3
+ metadata.gz: e0c99fb0a3faf8edcc73da84f74ce724e77b71a1
4
+ data.tar.gz: 4c50a4aa3b4b6bb70ae4f9f8d4305914c24e9f1b
5
5
  SHA512:
6
- metadata.gz: 0a4ab1f554f8445a4d7a283ba182a2c6b8244c5371295cd81478c24da0daaf00a71eeb477dfc1bd14dcb22f9b5ef28de938672edc8907a17fae3d0ac07c18b1c
7
- data.tar.gz: 68cc7911d99ee0161f3c09383d06f88ba482c4a0ab3e04f21049cdd94bbdac46e108598dde44ebff97a3671994a54679602a44727001363f2770ca8935e922be
6
+ metadata.gz: 717ee6c9d892e4d930464b2122ffbd2059420553c4b3f3ec670d6b66331d9885feeac96eb0edf7f2da1fcf11ef77bc05e3b2beacfbb7f96a19a7e780b2fb3ea5
7
+ data.tar.gz: 3b24edbe00fbbb46a20592bb5cc28bc5f00fa6fb05180a5b91405f2534c9c4a8d8e3b0b9ab12e1f6213a501f3cc2cab374e00a3bc3a31ba79281efa7e9e0a8d1
@@ -3,13 +3,11 @@ bundler_args: --without development
3
3
  before_script: "./bin/ci/before_build"
4
4
  script: "bundle exec rspec -cf documentation spec"
5
5
  rvm:
6
- - "2.1.0"
6
+ - "2.2"
7
+ - "2.1"
7
8
  - "2.0"
8
- - "1.9.3"
9
9
  - "jruby"
10
- - "1.9.2"
11
10
  - "rbx"
12
- - "1.8.7"
13
11
  notifications:
14
12
  email: michael@rabbitmq.com
15
13
  services:
@@ -21,6 +19,4 @@ branches:
21
19
  matrix:
22
20
  allow_failures:
23
21
  - rvm: rbx
24
- - rvm: "1.9.2"
25
- - rvm: "1.8.7"
26
22
  - rvm: jruby
@@ -1,4 +1,21 @@
1
- ## Changes between Bunny 1.7.0 and 1.8.0
1
+ ## Changes between Bunny 1.7.0 and 2.0.0
2
+
3
+ Bunny `2.0` doesn't have any breaking API changes
4
+ but drops Ruby 1.8 and 1.9 (both EOL'ed) support,
5
+ hence the version.
6
+
7
+ ### Minimum Required Ruby Version is 2.0
8
+
9
+ Bunny `2.0` requires Ruby 2.0 or later.
10
+
11
+ ## Non-Blocking Writes
12
+
13
+ Bunny now uses non-blocking socket writes, uses a reduced
14
+ number of writes for message publishing (frames are batched
15
+ into a single write), and handles TCP back pressure from
16
+ RabbitMQ better.
17
+
18
+ Contributed by Irina Bednova and Michael Klishin.
2
19
 
3
20
  ### Reduced Timeout Use
4
21
 
@@ -6,7 +23,23 @@
6
23
  numerous issues, including starting a new "interruptor" thread per operation,
7
24
  which is far from efficient.
8
25
 
9
- Contributed by Joe Eli McIlvain.
26
+ Contributed by Joe Eli McIlvain and Carl Hörberg.
27
+
28
+ ### Capped Number of Connection Recovery Attempts
29
+
30
+ `:recovery_attempts` is a new option that limits the number of
31
+ connection recovery attempts performed by Bunny. `nil` means
32
+ "no limit".
33
+
34
+ Contributed by Irina Bednova.
35
+
36
+ ### Bunny::Channel#basic_ack and Related Methods Improvements
37
+
38
+ `Bunny::Channel#basic_ack`, `Bunny::Channel#basic_nack`, and `Bunny::Channel#basic_reject`
39
+ now adjust delivery tags between connection recoveries, as well as have a default value for
40
+ the second argument.
41
+
42
+ Contributed by Wayne Conrad.
10
43
 
11
44
  ### Logger Output Remains Consistent
12
45
 
data/Gemfile CHANGED
@@ -19,8 +19,6 @@ extend Module.new {
19
19
  end
20
20
  }
21
21
 
22
- gem "SystemTimer", "~> 1.2.3", :platform => :ruby_18
23
-
24
22
  gem "rake", ">= 10.0.4"
25
23
  gem "effin_utf8"
26
24
 
data/README.md CHANGED
@@ -48,13 +48,15 @@ Specific examples:
48
48
 
49
49
  Modern Bunny versions support
50
50
 
51
- * CRuby 2.2, 2.1, 2.0, 1.9.3, and 1.8.7
51
+ * CRuby 2.2, 2.1, 2.0
52
52
  * Rubinius 2.0+
53
53
 
54
54
  Bunny works sufficiently well on JRuby but there are known
55
55
  JRuby bugs that cause high CPU burn. JRuby users should
56
56
  use [March Hare](http://rubymarchhare.info).
57
57
 
58
+ Bunny `1.7.x` was the last version to support CRuby 1.9.3 and 1.8.7
59
+
58
60
 
59
61
  ## Supported RabbitMQ Versions
60
62
 
@@ -468,9 +468,7 @@ module Bunny
468
468
  # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
469
469
  # @api public
470
470
  def reject(delivery_tag, requeue = false)
471
- guarding_against_stale_delivery_tags(delivery_tag) do
472
- basic_reject(delivery_tag.to_i, requeue)
473
- end
471
+ basic_reject(delivery_tag.to_i, requeue)
474
472
  end
475
473
 
476
474
  # Acknowledges a message. Acknowledged messages are completely removed from the queue.
@@ -481,9 +479,7 @@ module Bunny
481
479
  # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
482
480
  # @api public
483
481
  def ack(delivery_tag, multiple = false)
484
- guarding_against_stale_delivery_tags(delivery_tag) do
485
- basic_ack(delivery_tag.to_i, multiple)
486
- end
482
+ basic_ack(delivery_tag.to_i, multiple)
487
483
  end
488
484
  alias acknowledge ack
489
485
 
@@ -498,9 +494,7 @@ module Bunny
498
494
  # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
499
495
  # @api public
500
496
  def nack(delivery_tag, multiple = false, requeue = false)
501
- guarding_against_stale_delivery_tags(delivery_tag) do
502
- basic_nack(delivery_tag.to_i, multiple, requeue)
503
- end
497
+ basic_nack(delivery_tag.to_i, multiple, requeue)
504
498
  end
505
499
 
506
500
  # @endgroup
@@ -570,7 +564,7 @@ module Bunny
570
564
  opts[:mandatory],
571
565
  false,
572
566
  @connection.frame_max)
573
- @connection.send_frameset_without_timeout(frames, self)
567
+ @connection.send_frameset(frames, self)
574
568
 
575
569
  self
576
570
  end
@@ -707,11 +701,13 @@ module Bunny
707
701
  # @see Bunny::Channel#basic_nack
708
702
  # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
709
703
  # @api public
710
- def basic_reject(delivery_tag, requeue)
711
- raise_if_no_longer_open!
712
- @connection.send_frame(AMQ::Protocol::Basic::Reject.encode(@id, delivery_tag, requeue))
704
+ def basic_reject(delivery_tag, requeue = false)
705
+ guarding_against_stale_delivery_tags(delivery_tag) do
706
+ raise_if_no_longer_open!
707
+ @connection.send_frame(AMQ::Protocol::Basic::Reject.encode(@id, delivery_tag, requeue))
713
708
 
714
- nil
709
+ nil
710
+ end
715
711
  end
716
712
 
717
713
  # Acknowledges a delivery (message).
@@ -727,7 +723,7 @@ module Bunny
727
723
  # ch = conn.create_channel
728
724
  # q.subscribe do |delivery_info, properties, payload|
729
725
  # # requeue the message
730
- # ch.basic_ack(delivery_info.delivery_tag)
726
+ # ch.basic_ack(delivery_info.delivery_tag.to_i)
731
727
  # end
732
728
  #
733
729
  # @example Ack a message fetched via basic.get
@@ -737,7 +733,7 @@ module Bunny
737
733
  # ch = conn.create_channel
738
734
  # # we assume the queue exists and has messages
739
735
  # delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
740
- # ch.basic_ack(delivery_info.delivery_tag)
736
+ # ch.basic_ack(delivery_info.delivery_tag.to_i)
741
737
  #
742
738
  # @example Ack multiple messages fetched via basic.get
743
739
  # conn = Bunny.new
@@ -749,16 +745,17 @@ module Bunny
749
745
  # _, _, payload2 = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
750
746
  # delivery_info, properties, payload3 = ch.basic_get("bunny.examples.queue3", :manual_ack => true)
751
747
  # # ack all fetched messages up to payload3
752
- # ch.basic_ack(delivery_info.delivery_tag, true)
748
+ # ch.basic_ack(delivery_info.delivery_tag.to_i, true)
753
749
  #
754
750
  # @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
755
- # @see #basic_ack_known_delivery_tag
756
751
  # @api public
757
- def basic_ack(delivery_tag, multiple)
758
- raise_if_no_longer_open!
759
- @connection.send_frame(AMQ::Protocol::Basic::Ack.encode(@id, delivery_tag, multiple))
752
+ def basic_ack(delivery_tag, multiple = false)
753
+ guarding_against_stale_delivery_tags(delivery_tag) do
754
+ raise_if_no_longer_open!
755
+ @connection.send_frame(AMQ::Protocol::Basic::Ack.encode(@id, delivery_tag, multiple))
760
756
 
761
- nil
757
+ nil
758
+ end
762
759
  end
763
760
 
764
761
  # Rejects or requeues messages just like {Bunny::Channel#basic_reject} but can do so
@@ -815,13 +812,15 @@ module Bunny
815
812
  # @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
816
813
  # @api public
817
814
  def basic_nack(delivery_tag, multiple = false, requeue = false)
818
- raise_if_no_longer_open!
819
- @connection.send_frame(AMQ::Protocol::Basic::Nack.encode(@id,
820
- delivery_tag,
821
- multiple,
822
- requeue))
815
+ guarding_against_stale_delivery_tags(delivery_tag) do
816
+ raise_if_no_longer_open!
817
+ @connection.send_frame(AMQ::Protocol::Basic::Nack.encode(@id,
818
+ delivery_tag,
819
+ multiple,
820
+ requeue))
823
821
 
824
- nil
822
+ nil
823
+ end
825
824
  end
826
825
 
827
826
  # Registers a consumer for queue. Delivered messages will be handled with the block
@@ -6,27 +6,24 @@ module Bunny
6
6
  #
7
7
  # Heavily inspired by Dalli by Mike Perham.
8
8
  # @private
9
- class Socket < TCPSocket
9
+ module Socket
10
10
  attr_accessor :options
11
11
 
12
- # IO::WaitReadable is 1.9+ only
13
- READ_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK]
14
- READ_RETRY_EXCEPTION_CLASSES << IO::WaitReadable if IO.const_defined?(:WaitReadable)
15
-
16
- # IO::WaitWritable is 1.9+ only
17
- WRITE_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK]
18
- WRITE_RETRY_EXCEPTION_CLASSES << IO::WaitWritable if IO.const_defined?(:WaitWritable)
12
+ READ_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable]
13
+ WRITE_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable]
19
14
 
20
15
  def self.open(host, port, options = {})
21
- Timeout.timeout(options[:connect_timeout], ClientTimeout) do
22
- sock = new(host, port)
23
- if ::Socket.constants.include?('TCP_NODELAY') || ::Socket.constants.include?(:TCP_NODELAY)
24
- sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
25
- end
26
- sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) if options.fetch(:keepalive, true)
27
- sock.options = {:host => host, :port => port}.merge(options)
28
- sock
16
+ socket = ::Socket.tcp(host, port, nil, nil,
17
+ connect_timeout: options[:connect_timeout])
18
+ if ::Socket.constants.include?('TCP_NODELAY') || ::Socket.constants.include?(:TCP_NODELAY)
19
+ socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
29
20
  end
21
+ socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) if options.fetch(:keepalive, true)
22
+ socket.extend self
23
+ socket.options = { :host => host, :port => port }.merge(options)
24
+ socket
25
+ rescue Errno::ETIMEDOUT
26
+ raise ClientTimeout
30
27
  end
31
28
 
32
29
  # Reads given number of bytes with an optional timeout
@@ -8,13 +8,8 @@ module Bunny
8
8
  # methods found in Bunny::Socket.
9
9
  class SSLSocket < OpenSSL::SSL::SSLSocket
10
10
 
11
- # IO::WaitReadable is 1.9+ only
12
- READ_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK]
13
- READ_RETRY_EXCEPTION_CLASSES << IO::WaitReadable if IO.const_defined?(:WaitReadable)
14
-
15
- # IO::WaitWritable is 1.9+ only
16
- WRITE_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK]
17
- WRITE_RETRY_EXCEPTION_CLASSES << IO::WaitWritable if IO.const_defined?(:WaitWritable)
11
+ READ_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable]
12
+ WRITE_RETRY_EXCEPTION_CLASSES = [Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable]
18
13
 
19
14
  # Reads given number of bytes with an optional timeout
20
15
  #
@@ -46,11 +46,7 @@ module Bunny
46
46
  CONNECT_TIMEOUT = Transport::DEFAULT_CONNECTION_TIMEOUT
47
47
 
48
48
  # @private
49
- DEFAULT_CONTINUATION_TIMEOUT = if RUBY_VERSION.to_f < 1.9
50
- 8000
51
- else
52
- 4000
53
- end
49
+ DEFAULT_CONTINUATION_TIMEOUT = 15000
54
50
 
55
51
  # RabbitMQ client metadata
56
52
  DEFAULT_CLIENT_PROPERTIES = {
@@ -117,6 +113,8 @@ module Bunny
117
113
  # @option connection_string_or_opts [IO, String] :log_file The file or path to use when creating a logger. Defaults to STDOUT.
118
114
  # @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.
119
115
  # @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
120
118
  #
121
119
  # @option optz [String] :auth_mechanism ("PLAIN") Authentication mechanism, PLAIN or EXTERNAL
122
120
  # @option optz [String] :locale ("PLAIN") Locale RabbitMQ should use
@@ -156,6 +154,7 @@ module Bunny
156
154
  else
157
155
  opts[:automatically_recover] || opts[:automatic_recovery]
158
156
  end
157
+ @recovery_attempts = opts[:recovery_attempts]
159
158
  @network_recovery_interval = opts.fetch(:network_recovery_interval, DEFAULT_NETWORK_RECOVERY_INTERVAL)
160
159
  @recover_from_connection_close = opts.fetch(:recover_from_connection_close, false)
161
160
  # in ms
@@ -665,10 +664,18 @@ module Bunny
665
664
  rescue TCPConnectionFailedForAllHosts, TCPConnectionFailed, AMQ::Protocol::EmptyResponseError => e
666
665
  @logger.warn "TCP connection failed, reconnecting in #{@network_recovery_interval} seconds"
667
666
  sleep @network_recovery_interval
668
- retry if recoverable_network_failure?(e)
667
+ if should_retry_recovery?
668
+ @recovery_attempts -= 1 if @recovery_attempts
669
+ retry if recoverable_network_failure?(e)
670
+ end
669
671
  end
670
672
  end
671
673
 
674
+ # @private
675
+ def should_retry_recovery?
676
+ @recovery_attempts.nil? || @recovery_attempts > 1
677
+ end
678
+
672
679
  # @private
673
680
  def recover_channels
674
681
  # default channel is reopened right after connection
@@ -898,7 +905,9 @@ module Bunny
898
905
  # If we synchronize on the channel, however, this is both thread safe and pretty fine-grained
899
906
  # locking. Note that "single frame" methods do not need this kind of synchronization. MK.
900
907
  channel.synchronize do
901
- frames.each { |frame| self.send_frame(frame, false) }
908
+ # see rabbitmq/rabbitmq-server#156
909
+ data = frames.reduce("") { |acc, frame| acc << frame.encode }
910
+ @transport.write(data)
902
911
  signal_activity!
903
912
  end
904
913
  end # send_frameset(frames)
@@ -1,16 +1,5 @@
1
1
  module Bunny
2
- # Unifies Ruby standard library's Timeout (which is not accurate on
3
- # Ruby 1.8) and SystemTimer (the gem)
4
- Timeout = if RUBY_VERSION < "1.9"
5
- begin
6
- require "bunny/system_timer"
7
- Bunny::SystemTimer
8
- rescue LoadError
9
- Timeout
10
- end
11
- else
12
- Timeout
13
- end
2
+ Timeout = ::Timeout
14
3
 
15
4
  # Backwards compatibility
16
5
  # @private
@@ -20,10 +20,10 @@ module Bunny
20
20
  #
21
21
 
22
22
  # Default TCP connection timeout
23
- DEFAULT_CONNECTION_TIMEOUT = 25.0
23
+ DEFAULT_CONNECTION_TIMEOUT = 30.0
24
24
 
25
- DEFAULT_READ_TIMEOUT = 5.0
26
- DEFAULT_WRITE_TIMEOUT = 5.0
25
+ DEFAULT_READ_TIMEOUT = 30.0
26
+ DEFAULT_WRITE_TIMEOUT = 30.0
27
27
 
28
28
  attr_reader :session, :host, :port, :socket, :connect_timeout, :read_timeout, :write_timeout, :disconnect_timeout
29
29
  attr_reader :tls_context
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Bunny
4
4
  # @return [String] Version of the library
5
- VERSION = "2.0.0.rc1"
5
+ VERSION = "2.0.0.rc2"
6
6
  end
@@ -23,12 +23,60 @@ describe Bunny::Channel, "#ack" do
23
23
  delivery_details, properties, content = q.pop(:manual_ack => true)
24
24
 
25
25
  ch.ack(delivery_details.delivery_tag, true)
26
+ ch.close
27
+
28
+ ch = connection.create_channel
29
+ q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true)
26
30
  expect(q.message_count).to eq 0
31
+ ch.close
32
+ end
33
+ end
27
34
 
35
+ context "with a valid (known) delivery tag (multiple = true)" do
36
+ it "acknowledges a message" do
37
+ ch = connection.create_channel
38
+ q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true)
39
+ x = ch.default_exchange
40
+
41
+ x.publish("bunneth", :routing_key => q.name)
42
+ x.publish("bunneth", :routing_key => q.name)
43
+ sleep 0.5
44
+ expect(q.message_count).to eq 2
45
+ delivery_details_1, _properties, _content = q.pop(:manual_ack => true)
46
+ delivery_details_2, _properties, _content = q.pop(:manual_ack => true)
47
+
48
+ ch.ack(delivery_details_2.delivery_tag, true)
49
+ ch.close
50
+
51
+ ch = connection.create_channel
52
+ q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true)
53
+ expect(q.message_count).to eq 0
28
54
  ch.close
29
55
  end
30
56
  end
31
57
 
58
+ context "with a valid (known) delivery tag (multiple = false)" do
59
+ it "acknowledges a message" do
60
+ ch = connection.create_channel
61
+ q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true)
62
+ x = ch.default_exchange
63
+
64
+ x.publish("bunneth", :routing_key => q.name)
65
+ x.publish("bunneth", :routing_key => q.name)
66
+ sleep 0.5
67
+ expect(q.message_count).to eq 2
68
+ delivery_details_1, _properties, _content = q.pop(:manual_ack => true)
69
+ delivery_details_2, _properties, _content = q.pop(:manual_ack => true)
70
+
71
+ ch.ack(delivery_details_2.delivery_tag, false)
72
+ ch.close
73
+
74
+ ch = connection.create_channel
75
+ q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true)
76
+ expect(q.message_count).to eq 1
77
+ ch.close
78
+ end
79
+ end
32
80
 
33
81
  context "with a valid (known) delivery tag and automatic ack mode" do
34
82
  it "results in a channel exception" do
@@ -90,8 +138,92 @@ describe Bunny::Channel, "#ack" do
90
138
  $stderr = orig_stderr
91
139
 
92
140
  ch.ack(delivery_details.delivery_tag, true)
141
+ ch.close
142
+
143
+ ch = connection.create_channel
144
+ q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true)
145
+ expect(q.message_count).to eq 0
146
+ ch.close
147
+ end
148
+ end
149
+ end
150
+
151
+ describe Bunny::Channel, "#basic_ack" do
152
+ let(:connection) do
153
+ c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed")
154
+ c.start
155
+ c
156
+ end
157
+
158
+ after :each do
159
+ connection.close if connection.open?
160
+ end
161
+
162
+ context "with a valid (known) delivery tag (multiple = true)" do
163
+ it "acknowledges a message" do
164
+ ch = connection.create_channel
165
+ q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true)
166
+ x = ch.default_exchange
167
+
168
+ x.publish("bunneth", :routing_key => q.name)
169
+ x.publish("bunneth", :routing_key => q.name)
170
+ sleep 0.5
171
+ expect(q.message_count).to eq 2
172
+ delivery_details_1, _properties, _content = q.pop(:manual_ack => true)
173
+ delivery_details_2, _properties, _content = q.pop(:manual_ack => true)
174
+
175
+ ch.basic_ack(delivery_details_2.delivery_tag.to_i, true)
176
+ ch.close
177
+
178
+ ch = connection.create_channel
179
+ q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true)
93
180
  expect(q.message_count).to eq 0
181
+ ch.close
182
+ end
183
+ end
94
184
 
185
+ context "with a valid (known) delivery tag (multiple = false)" do
186
+ it "acknowledges a message" do
187
+ ch = connection.create_channel
188
+ q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true)
189
+ x = ch.default_exchange
190
+
191
+ x.publish("bunneth", :routing_key => q.name)
192
+ x.publish("bunneth", :routing_key => q.name)
193
+ sleep 0.5
194
+ expect(q.message_count).to eq 2
195
+ delivery_details_1, _properties, _content = q.pop(:manual_ack => true)
196
+ delivery_details_2, _properties, _content = q.pop(:manual_ack => true)
197
+
198
+ ch.basic_ack(delivery_details_2.delivery_tag.to_i, false)
199
+ ch.close
200
+
201
+ ch = connection.create_channel
202
+ q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true)
203
+ expect(q.message_count).to eq 1
204
+ ch.close
205
+ end
206
+ end
207
+
208
+ context "with a valid (known) delivery tag (multiple = default)" do
209
+ it "acknowledges a message" do
210
+ ch = connection.create_channel
211
+ q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true)
212
+ x = ch.default_exchange
213
+
214
+ x.publish("bunneth", :routing_key => q.name)
215
+ x.publish("bunneth", :routing_key => q.name)
216
+ sleep 0.5
217
+ expect(q.message_count).to eq 2
218
+ delivery_details_1, _properties, _content = q.pop(:manual_ack => true)
219
+ delivery_details_2, _properties, _content = q.pop(:manual_ack => true)
220
+
221
+ ch.basic_ack(delivery_details_2.delivery_tag.to_i)
222
+ ch.close
223
+
224
+ ch = connection.create_channel
225
+ q = ch.queue("bunny.basic.ack.manual-acks", :exclusive => true)
226
+ expect(q.message_count).to eq 1
95
227
  ch.close
96
228
  end
97
229
  end
@@ -27,9 +27,12 @@ describe Bunny::Channel, "#nack" do
27
27
 
28
28
  subject.nack(delivery_info.delivery_tag, false, false)
29
29
  sleep(0.5)
30
- expect(q.message_count).to eq 0
31
-
32
30
  subject.close
31
+
32
+ ch = connection.create_channel
33
+ q = ch.queue("bunny.basic.nack.with-requeue-false", :exclusive => true)
34
+ expect(q.message_count).to eq 0
35
+ ch.close
33
36
  end
34
37
  end
35
38
 
@@ -1,94 +1,5 @@
1
1
  require "spec_helper"
2
2
 
3
- if RUBY_VERSION <= "1.9"
4
- describe "Publishing a message to the default exchange" do
5
- let(:connection) do
6
- c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed")
7
- c.start
8
- c
9
- end
10
-
11
- after :each do
12
- connection.close if connection.open?
13
- end
14
-
15
-
16
- context "with all default delivery and message properties" do
17
- it "routes messages to a queue with the same name as the routing key" do
18
- expect(connection).to be_threaded
19
- ch = connection.create_channel
20
-
21
- q = ch.queue("", :exclusive => true)
22
- x = ch.default_exchange
23
-
24
- x.publish("xyzzy", :routing_key => q.name).
25
- publish("xyzzy", :routing_key => q.name).
26
- publish("xyzzy", :routing_key => q.name).
27
- publish("xyzzy", :routing_key => q.name)
28
-
29
- sleep(1)
30
- expect(q.message_count).to eq 4
31
-
32
- ch.close
33
- end
34
- end
35
-
36
-
37
- context "with all default delivery and message properties" do
38
- it "routes the messages and preserves all the metadata" do
39
- expect(connection).to be_threaded
40
- ch = connection.create_channel
41
-
42
- q = ch.queue("", :exclusive => true)
43
- x = ch.default_exchange
44
-
45
- x.publish("xyzzy", :routing_key => q.name, :persistent => true)
46
-
47
- sleep(1)
48
- expect(q.message_count).to eq 1
49
-
50
- envelope, headers, payload = q.pop
51
-
52
- expect(payload).to eq "xyzzy"
53
-
54
- expect(headers[:content_type]).to eq "application/octet-stream"
55
- expect(headers[:delivery_mode]).to eq 2
56
- expect(headers[:priority]).to eq 0
57
-
58
- ch.close
59
- end
60
- end
61
-
62
-
63
- context "with all default delivery and message properties on a single-threaded connection" do
64
- let(:connection) do
65
- c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed", :threaded => false)
66
- c.start
67
- c
68
- end
69
-
70
- it "routes messages to a queue with the same name as the routing key" do
71
- expect(connection).not_to be_threaded
72
- ch = connection.create_channel
73
-
74
- q = ch.queue("", :exclusive => true)
75
- x = ch.default_exchange
76
-
77
- x.publish("xyzzy", :routing_key => q.name).
78
- publish("xyzzy", :routing_key => q.name).
79
- publish("xyzzy", :routing_key => q.name).
80
- publish("xyzzy", :routing_key => q.name)
81
-
82
- sleep(1)
83
- expect(q.message_count).to eq 4
84
-
85
- ch.close
86
- end
87
- end
88
- end
89
- end
90
-
91
-
92
3
  describe "Published message" do
93
4
  let(:connection) do
94
5
  c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed")
@@ -43,8 +43,11 @@ describe Bunny::Channel, "#reject" do
43
43
 
44
44
  ch.reject(delivery_info.delivery_tag, false)
45
45
  sleep(0.5)
46
- expect(q.message_count).to eq 0
46
+ ch.close
47
47
 
48
+ ch = connection.create_channel
49
+ q = ch.queue("bunny.basic.reject.with-requeue-false", :exclusive => true)
50
+ expect(q.message_count).to eq 0
48
51
  ch.close
49
52
  end
50
53
  end
@@ -72,3 +75,78 @@ describe Bunny::Channel, "#reject" do
72
75
  end
73
76
  end
74
77
  end
78
+
79
+ describe Bunny::Channel, "#basic_reject" do
80
+ let(:connection) do
81
+ c = Bunny.new(:user => "bunny_gem", :password => "bunny_password", :vhost => "bunny_testbed")
82
+ c.start
83
+ c
84
+ end
85
+
86
+ after :each do
87
+ connection.close if connection.open?
88
+ end
89
+
90
+ context "with requeue = true" do
91
+ it "requeues a message" do
92
+ ch = connection.create_channel
93
+ q = ch.queue("bunny.basic.reject.manual-acks", :exclusive => true)
94
+ x = ch.default_exchange
95
+
96
+ x.publish("bunneth", :routing_key => q.name)
97
+ sleep(0.5)
98
+ expect(q.message_count).to eq 1
99
+ delivery_info, _, _ = q.pop(:manual_ack => true)
100
+
101
+ ch.basic_reject(delivery_info.delivery_tag.to_i, true)
102
+ sleep(0.5)
103
+ expect(q.message_count).to eq 1
104
+
105
+ ch.close
106
+ end
107
+ end
108
+
109
+ context "with requeue = false" do
110
+ it "rejects a message" do
111
+ ch = connection.create_channel
112
+ q = ch.queue("bunny.basic.reject.with-requeue-false", :exclusive => true)
113
+ x = ch.default_exchange
114
+
115
+ x.publish("bunneth", :routing_key => q.name)
116
+ sleep(0.5)
117
+ expect(q.message_count).to eq 1
118
+ delivery_info, _, _ = q.pop(:manual_ack => true)
119
+
120
+ ch.basic_reject(delivery_info.delivery_tag.to_i, false)
121
+ sleep(0.5)
122
+ ch.close
123
+
124
+ ch = connection.create_channel
125
+ q = ch.queue("bunny.basic.reject.with-requeue-false", :exclusive => true)
126
+ expect(q.message_count).to eq 0
127
+ ch.close
128
+ end
129
+ end
130
+
131
+ context "with requeue = default" do
132
+ it "rejects a message" do
133
+ ch = connection.create_channel
134
+ q = ch.queue("bunny.basic.reject.with-requeue-false", :exclusive => true)
135
+ x = ch.default_exchange
136
+
137
+ x.publish("bunneth", :routing_key => q.name)
138
+ sleep(0.5)
139
+ expect(q.message_count).to eq 1
140
+ delivery_info, _, _ = q.pop(:manual_ack => true)
141
+
142
+ ch.basic_reject(delivery_info.delivery_tag.to_i)
143
+ sleep(0.5)
144
+ ch.close
145
+
146
+ ch = connection.create_channel
147
+ q = ch.queue("bunny.basic.reject.with-requeue-false", :exclusive => true)
148
+ expect(q.message_count).to eq 0
149
+ ch.close
150
+ end
151
+ end
152
+ end
@@ -52,6 +52,16 @@ unless ENV["CI"]
52
52
  end
53
53
  end
54
54
 
55
+ def with_recovery_attempts_limited_to(attempts = 3, &block)
56
+ c = Bunny.new(:recover_from_connection_close => true, :network_recovery_interval => 0.2, :recovery_attempts => attempts)
57
+ begin
58
+ c.start
59
+ block.call(c)
60
+ ensure
61
+ c.close
62
+ end
63
+ end
64
+
55
65
  def ensure_queue_recovery(ch, q)
56
66
  q.purge
57
67
  x = ch.default_exchange
@@ -353,5 +363,14 @@ unless ENV["CI"]
353
363
  end
354
364
  end
355
365
  end
366
+
367
+ it "tries to recover for a given number of attempts" do
368
+ with_recovery_attempts_limited_to(2) do |c|
369
+ close_all_connections!
370
+ expect(c).to receive(:start).exactly(2).times.and_raise(Bunny::TCPConnectionFailed.new("test"))
371
+
372
+ wait_for_recovery
373
+ end
374
+ end
356
375
  end
357
376
  end
@@ -14,19 +14,6 @@ require "rabbitmq/http/client"
14
14
  require "amq/protocol/version"
15
15
  puts "Using Ruby #{RUBY_VERSION}, amq-protocol #{AMQ::Protocol::VERSION}"
16
16
 
17
- #
18
- # Ruby version-specific
19
- #
20
-
21
- case RUBY_VERSION
22
- when "1.8.7" then
23
- class Array
24
- alias sample choice
25
- end
26
- when "1.8.6" then
27
- raise "Ruby 1.8.6 is not supported. Sorry, pal. Time to move on beyond One True Ruby. Yes, time flies by."
28
- end
29
-
30
17
  module RabbitMQ
31
18
  module Control
32
19
 
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.0.0.rc1
4
+ version: 2.0.0.rc2
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-06-12 00:00:00.000000000 Z
15
+ date: 2015-06-26 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: amq-protocol
@@ -128,7 +128,6 @@ files:
128
128
  - lib/bunny/session.rb
129
129
  - lib/bunny/socket.rb
130
130
  - lib/bunny/ssl_socket.rb
131
- - lib/bunny/system_timer.rb
132
131
  - lib/bunny/test_kit.rb
133
132
  - lib/bunny/timeout.rb
134
133
  - lib/bunny/transport.rb
@@ -212,7 +211,6 @@ files:
212
211
  - spec/unit/concurrent/condition_spec.rb
213
212
  - spec/unit/concurrent/linked_continuation_queue_spec.rb
214
213
  - spec/unit/concurrent/synchronized_sorted_set_spec.rb
215
- - spec/unit/system_timer_spec.rb
216
214
  - spec/unit/version_delivery_tag_spec.rb
217
215
  homepage: http://rubybunny.info
218
216
  licenses:
@@ -315,6 +313,5 @@ test_files:
315
313
  - spec/unit/concurrent/condition_spec.rb
316
314
  - spec/unit/concurrent/linked_continuation_queue_spec.rb
317
315
  - spec/unit/concurrent/synchronized_sorted_set_spec.rb
318
- - spec/unit/system_timer_spec.rb
319
316
  - spec/unit/version_delivery_tag_spec.rb
320
317
  has_rdoc: true
@@ -1,20 +0,0 @@
1
- # -*- encoding: utf-8; mode: ruby -*-
2
-
3
- require "system_timer"
4
-
5
- module Bunny
6
- # Used for Ruby before 1.9
7
- class SystemTimer
8
- # Executes a block of code, raising if the execution does not finish
9
- # in the alloted period of time, in seconds.
10
- def self.timeout(seconds, exception = nil)
11
- if seconds
12
- ::SystemTimer.timeout_after(seconds, exception) do
13
- yield
14
- end
15
- else
16
- yield
17
- end
18
- end
19
- end # SystemTimer
20
- end # Bunny
@@ -1,10 +0,0 @@
1
- require "spec_helper"
2
-
3
- if RUBY_VERSION.to_f < 1.9
4
- describe Bunny::SystemTimer do
5
- it 'supports being called with a single argument' do
6
- expect {Bunny::SystemTimer::timeout(1) {}}.not_to raise_error
7
- expect {Bunny::SystemTimer::timeout(1, nil) {}}.not_to raise_error
8
- end
9
- end
10
- end