bunny 2.24.0 → 3.1.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 +4 -4
- data/README.md +15 -9
- data/lib/bunny/channel.rb +727 -124
- data/lib/bunny/concurrent/exception_accumulator.rb +115 -0
- data/lib/bunny/consumer.rb +2 -11
- data/lib/bunny/cruby/socket.rb +33 -1
- data/lib/bunny/cruby/ssl_socket.rb +41 -0
- data/lib/bunny/delivery_info.rb +22 -16
- data/lib/bunny/exceptions.rb +31 -2
- data/lib/bunny/exchange.rb +25 -13
- data/lib/bunny/get_response.rb +19 -15
- data/lib/bunny/heartbeat_sender.rb +2 -2
- data/lib/bunny/queue.rb +22 -38
- data/lib/bunny/reader_loop.rb +6 -6
- data/lib/bunny/return_info.rb +16 -11
- data/lib/bunny/session.rb +388 -36
- data/lib/bunny/timestamp.rb +1 -1
- data/lib/bunny/topology_recovery_filter.rb +71 -0
- data/lib/bunny/topology_registry.rb +824 -0
- data/lib/bunny/transport.rb +36 -9
- data/lib/bunny/version.rb +1 -1
- data/lib/bunny.rb +1 -1
- metadata +29 -7
- data/lib/bunny/versioned_delivery_tag.rb +0 -30
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thread"
|
|
4
|
+
|
|
5
|
+
module Bunny
|
|
6
|
+
module Concurrent
|
|
7
|
+
# A thread-safe exception accumulator that stores exceptions for later retrieval
|
|
8
|
+
# instead of immediately raising them in the calling thread.
|
|
9
|
+
#
|
|
10
|
+
# This is the default session error handler in Bunny. When errors occur in
|
|
11
|
+
# background threads (such as the reader loop or transport), they are stored
|
|
12
|
+
# in the accumulator rather than being raised asynchronously in the thread
|
|
13
|
+
# that created the session.
|
|
14
|
+
#
|
|
15
|
+
# This prevents dangerous control flow interruptions that can occur when
|
|
16
|
+
# exceptions are raised asynchronously via Thread#raise.
|
|
17
|
+
#
|
|
18
|
+
# @example Checking for accumulated exceptions
|
|
19
|
+
# conn = Bunny.new
|
|
20
|
+
# conn.start
|
|
21
|
+
# # ... do work ...
|
|
22
|
+
# if conn.exception_occurred?
|
|
23
|
+
# exceptions = conn.exceptions
|
|
24
|
+
# # handle exceptions appropriately
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# @see https://github.com/ruby-amqp/bunny/issues/721
|
|
28
|
+
# @api public
|
|
29
|
+
class ExceptionAccumulator
|
|
30
|
+
def initialize
|
|
31
|
+
@exceptions = []
|
|
32
|
+
@mutex = Mutex.new
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Called by background threads to record an exception.
|
|
36
|
+
# This method is compatible with Thread#raise interface.
|
|
37
|
+
#
|
|
38
|
+
# @param exception [Exception] the exception to accumulate
|
|
39
|
+
def raise(exception)
|
|
40
|
+
@mutex.synchronize do
|
|
41
|
+
@exceptions << exception
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Returns true if any exceptions have been accumulated.
|
|
46
|
+
#
|
|
47
|
+
# @return [Boolean]
|
|
48
|
+
def any?
|
|
49
|
+
@mutex.synchronize { @exceptions.any? }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Returns true if no exceptions have been accumulated.
|
|
53
|
+
#
|
|
54
|
+
# @return [Boolean]
|
|
55
|
+
def empty?
|
|
56
|
+
@mutex.synchronize { @exceptions.empty? }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Returns the number of accumulated exceptions.
|
|
60
|
+
#
|
|
61
|
+
# @return [Integer]
|
|
62
|
+
def count
|
|
63
|
+
@mutex.synchronize { @exceptions.count }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Returns all accumulated exceptions.
|
|
67
|
+
#
|
|
68
|
+
# @return [Array<Exception>]
|
|
69
|
+
def all
|
|
70
|
+
@mutex.synchronize { @exceptions.dup }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Returns and removes the first accumulated exception (FIFO order).
|
|
74
|
+
# Returns nil if no exceptions have been accumulated.
|
|
75
|
+
#
|
|
76
|
+
# @return [Exception, nil]
|
|
77
|
+
def pop
|
|
78
|
+
@mutex.synchronize { @exceptions.shift }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Clears all accumulated exceptions.
|
|
82
|
+
#
|
|
83
|
+
# @return [Array<Exception>] the exceptions that were cleared
|
|
84
|
+
def clear
|
|
85
|
+
@mutex.synchronize do
|
|
86
|
+
cleared = @exceptions.dup
|
|
87
|
+
@exceptions.clear
|
|
88
|
+
cleared
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Raises the first accumulated exception if any exist, removing it from the accumulator.
|
|
93
|
+
# Does nothing if no exceptions have been accumulated.
|
|
94
|
+
#
|
|
95
|
+
# @raise [Exception] the first accumulated exception
|
|
96
|
+
def raise_first!
|
|
97
|
+
exception = pop
|
|
98
|
+
Kernel.raise exception if exception
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Raises all accumulated exceptions wrapped in a single exception if any exist,
|
|
102
|
+
# clearing the accumulator.
|
|
103
|
+
#
|
|
104
|
+
# @raise [Bunny::AccumulatedExceptions] wrapper containing all accumulated exceptions
|
|
105
|
+
def raise_all!
|
|
106
|
+
exceptions = clear
|
|
107
|
+
return if exceptions.empty?
|
|
108
|
+
Kernel.raise AccumulatedExceptions.new(exceptions)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Alias for backward compatibility and convenience
|
|
114
|
+
ExceptionAccumulator = Concurrent::ExceptionAccumulator
|
|
115
|
+
end
|
data/lib/bunny/consumer.rb
CHANGED
|
@@ -99,13 +99,13 @@ module Bunny
|
|
|
99
99
|
# @return [Boolean] true if this consumer uses automatic acknowledgement mode
|
|
100
100
|
# @api public
|
|
101
101
|
def automatic_acknowledgement?
|
|
102
|
-
@no_ack
|
|
102
|
+
@no_ack
|
|
103
103
|
end
|
|
104
104
|
|
|
105
105
|
# @return [Boolean] true if this consumer uses manual (explicit) acknowledgement mode
|
|
106
106
|
# @api public
|
|
107
107
|
def manual_acknowledgement?
|
|
108
|
-
|
|
108
|
+
!@no_ack
|
|
109
109
|
end
|
|
110
110
|
|
|
111
111
|
# @return [String] Name of the queue this consumer is on
|
|
@@ -117,14 +117,5 @@ module Bunny
|
|
|
117
117
|
@queue
|
|
118
118
|
end
|
|
119
119
|
end
|
|
120
|
-
|
|
121
|
-
#
|
|
122
|
-
# Recovery
|
|
123
|
-
#
|
|
124
|
-
|
|
125
|
-
# @private
|
|
126
|
-
def recover_from_network_failure
|
|
127
|
-
@channel.basic_consume_with(self)
|
|
128
|
-
end
|
|
129
120
|
end
|
|
130
121
|
end
|
data/lib/bunny/cruby/socket.rb
CHANGED
|
@@ -29,7 +29,8 @@ module Bunny
|
|
|
29
29
|
|
|
30
30
|
def self.open(host, port, options = {})
|
|
31
31
|
socket = ::Socket.tcp(host, port, nil, nil,
|
|
32
|
-
connect_timeout: options[:connect_timeout]
|
|
32
|
+
connect_timeout: options[:connect_timeout],
|
|
33
|
+
resolv_timeout: options[:connect_timeout])
|
|
33
34
|
if ::Socket.constants.include?('TCP_NODELAY') || ::Socket.constants.include?(:TCP_NODELAY)
|
|
34
35
|
socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
|
|
35
36
|
end
|
|
@@ -73,6 +74,37 @@ module Bunny
|
|
|
73
74
|
value
|
|
74
75
|
end # read_fully
|
|
75
76
|
|
|
77
|
+
# Reads given number of bytes into an existing buffer with an optional timeout
|
|
78
|
+
#
|
|
79
|
+
# @param [String] buffer Buffer to read into (will be appended to)
|
|
80
|
+
# @param [Integer] count How many bytes to read
|
|
81
|
+
# @param [Integer] timeout Timeout
|
|
82
|
+
#
|
|
83
|
+
# @return [String] The buffer with data appended
|
|
84
|
+
# @api public
|
|
85
|
+
def read_fully_into(buffer, count, timeout = nil)
|
|
86
|
+
return nil if @__bunny_socket_eof_flag__
|
|
87
|
+
|
|
88
|
+
bytes_read = 0
|
|
89
|
+
begin
|
|
90
|
+
loop do
|
|
91
|
+
chunk = read_nonblock(count - bytes_read)
|
|
92
|
+
buffer << chunk
|
|
93
|
+
bytes_read += chunk.bytesize
|
|
94
|
+
break if bytes_read >= count
|
|
95
|
+
end
|
|
96
|
+
rescue EOFError
|
|
97
|
+
@__bunny_socket_eof_flag__ = true
|
|
98
|
+
rescue *READ_RETRY_EXCEPTION_CLASSES
|
|
99
|
+
if IO.select([self], nil, nil, timeout)
|
|
100
|
+
retry
|
|
101
|
+
else
|
|
102
|
+
raise Timeout::Error, "IO timeout when reading #{count} bytes"
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
buffer
|
|
106
|
+
end # read_fully_into
|
|
107
|
+
|
|
76
108
|
# Writes provided data using IO#write_nonblock, taking care of handling
|
|
77
109
|
# of exceptions it raises when writing would fail (e.g. due to socket buffer
|
|
78
110
|
# being full).
|
|
@@ -69,6 +69,47 @@ module Bunny
|
|
|
69
69
|
value
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
+
# Reads given number of bytes into an existing buffer with an optional timeout
|
|
73
|
+
#
|
|
74
|
+
# @param [String] buffer Buffer to read into (will be appended to)
|
|
75
|
+
# @param [Integer] count How many bytes to read
|
|
76
|
+
# @param [Integer] timeout Timeout
|
|
77
|
+
#
|
|
78
|
+
# @return [String] The buffer with data appended
|
|
79
|
+
# @api public
|
|
80
|
+
def read_fully_into(buffer, count, timeout = nil)
|
|
81
|
+
return nil if @__bunny_socket_eof_flag__
|
|
82
|
+
|
|
83
|
+
bytes_read = 0
|
|
84
|
+
begin
|
|
85
|
+
loop do
|
|
86
|
+
chunk = read_nonblock(count - bytes_read)
|
|
87
|
+
buffer << chunk
|
|
88
|
+
bytes_read += chunk.bytesize
|
|
89
|
+
break if bytes_read >= count
|
|
90
|
+
end
|
|
91
|
+
rescue EOFError
|
|
92
|
+
@__bunny_socket_eof_flag__ = true
|
|
93
|
+
rescue OpenSSL::SSL::SSLError => e
|
|
94
|
+
if e.message == "read would block"
|
|
95
|
+
if IO.select([self], nil, nil, timeout)
|
|
96
|
+
retry
|
|
97
|
+
else
|
|
98
|
+
raise Timeout::Error, "IO timeout when reading #{count} bytes"
|
|
99
|
+
end
|
|
100
|
+
else
|
|
101
|
+
raise e
|
|
102
|
+
end
|
|
103
|
+
rescue *READ_RETRY_EXCEPTION_CLASSES
|
|
104
|
+
if IO.select([self], nil, nil, timeout)
|
|
105
|
+
retry
|
|
106
|
+
else
|
|
107
|
+
raise Timeout::Error, "IO timeout when reading #{count} bytes"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
buffer
|
|
111
|
+
end
|
|
112
|
+
|
|
72
113
|
# Writes provided data using IO#write_nonblock, taking care of handling
|
|
73
114
|
# of exceptions it raises when writing would fail (e.g. due to socket buffer
|
|
74
115
|
# being full).
|
data/lib/bunny/delivery_info.rb
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "bunny/versioned_delivery_tag"
|
|
4
|
-
|
|
5
3
|
module Bunny
|
|
6
|
-
# Wraps
|
|
4
|
+
# Wraps [AMQ::Protocol::Basic::Deliver] to
|
|
7
5
|
# provide access to the delivery properties as immutable hash as
|
|
8
|
-
# well as methods.
|
|
6
|
+
# well as methods. Hash representation is created lazily.
|
|
9
7
|
class DeliveryInfo
|
|
10
8
|
|
|
11
9
|
#
|
|
@@ -26,15 +24,6 @@ module Bunny
|
|
|
26
24
|
# @private
|
|
27
25
|
def initialize(basic_deliver, consumer, channel)
|
|
28
26
|
@basic_deliver = basic_deliver
|
|
29
|
-
@hash = {
|
|
30
|
-
:consumer_tag => basic_deliver.consumer_tag,
|
|
31
|
-
:delivery_tag => VersionedDeliveryTag.new(basic_deliver.delivery_tag, channel.recoveries_counter),
|
|
32
|
-
:redelivered => basic_deliver.redelivered,
|
|
33
|
-
:exchange => basic_deliver.exchange,
|
|
34
|
-
:routing_key => basic_deliver.routing_key,
|
|
35
|
-
:consumer => consumer,
|
|
36
|
-
:channel => channel
|
|
37
|
-
}
|
|
38
27
|
@consumer = consumer
|
|
39
28
|
@channel = channel
|
|
40
29
|
end
|
|
@@ -42,18 +31,35 @@ module Bunny
|
|
|
42
31
|
# Iterates over delivery properties
|
|
43
32
|
# @see Enumerable#each
|
|
44
33
|
def each(*args, &block)
|
|
45
|
-
|
|
34
|
+
to_hash.each(*args, &block)
|
|
46
35
|
end
|
|
47
36
|
|
|
48
37
|
# Accesses delivery properties by key
|
|
49
38
|
# @see Hash#[]
|
|
50
39
|
def [](k)
|
|
51
|
-
|
|
40
|
+
case k
|
|
41
|
+
when :consumer_tag then @basic_deliver.consumer_tag
|
|
42
|
+
when :delivery_tag then @basic_deliver.delivery_tag
|
|
43
|
+
when :redelivered then @basic_deliver.redelivered
|
|
44
|
+
when :exchange then @basic_deliver.exchange
|
|
45
|
+
when :routing_key then @basic_deliver.routing_key
|
|
46
|
+
when :consumer then @consumer
|
|
47
|
+
when :channel then @channel
|
|
48
|
+
else nil
|
|
49
|
+
end
|
|
52
50
|
end
|
|
53
51
|
|
|
54
52
|
# @return [Hash] Hash representation of this delivery info
|
|
55
53
|
def to_hash
|
|
56
|
-
@hash
|
|
54
|
+
@hash ||= {
|
|
55
|
+
:consumer_tag => @basic_deliver.consumer_tag,
|
|
56
|
+
:delivery_tag => @basic_deliver.delivery_tag,
|
|
57
|
+
:redelivered => @basic_deliver.redelivered,
|
|
58
|
+
:exchange => @basic_deliver.exchange,
|
|
59
|
+
:routing_key => @basic_deliver.routing_key,
|
|
60
|
+
:consumer => @consumer,
|
|
61
|
+
:channel => @channel
|
|
62
|
+
}
|
|
57
63
|
end
|
|
58
64
|
|
|
59
65
|
# @private
|
data/lib/bunny/exceptions.rb
CHANGED
|
@@ -6,6 +6,9 @@ module Bunny
|
|
|
6
6
|
class Exception < ::StandardError
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
# Used when a list of endpoints (hostnames) to connect to
|
|
10
|
+
# has been fully traversed, that is, there are no more endpoints (hostnames)
|
|
11
|
+
# to try.
|
|
9
12
|
class HostListDepleted < Exception
|
|
10
13
|
def initialize
|
|
11
14
|
super("No more hosts to try in the supplied list of hosts")
|
|
@@ -163,7 +166,7 @@ module Bunny
|
|
|
163
166
|
class MessageError < ConnectionLevelException; end
|
|
164
167
|
# @private
|
|
165
168
|
class ProtocolError < ConnectionLevelException; end
|
|
166
|
-
# Raised when RabbitMQ reports
|
|
169
|
+
# Raised when RabbitMQ reports an internal error
|
|
167
170
|
class InternalError < ConnectionLevelException; end
|
|
168
171
|
|
|
169
172
|
# Raised when read or write I/O operations time out (but only if
|
|
@@ -177,7 +180,7 @@ module Bunny
|
|
|
177
180
|
class InconsistentDataError < Exception
|
|
178
181
|
end
|
|
179
182
|
|
|
180
|
-
# Raised by adapters when frame does not end with
|
|
183
|
+
# Raised by adapters when frame does not end with final octet [AMQ::Protocol::Frame::FINAL_OCTET].
|
|
181
184
|
# This suggest that there is a bug in adapter or AMQ broker implementation.
|
|
182
185
|
#
|
|
183
186
|
# @see https://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf AMQP 0.9.1 specification (Section 2.3)
|
|
@@ -209,6 +212,18 @@ module Bunny
|
|
|
209
212
|
end
|
|
210
213
|
end
|
|
211
214
|
|
|
215
|
+
# Raised when a published message is nacked by the broker
|
|
216
|
+
# and publisher confirm tracking is enabled.
|
|
217
|
+
# @api public
|
|
218
|
+
class MessageNacked < Exception
|
|
219
|
+
attr_reader :delivery_tag
|
|
220
|
+
|
|
221
|
+
def initialize(message, delivery_tag)
|
|
222
|
+
super(message)
|
|
223
|
+
@delivery_tag = delivery_tag
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
212
227
|
# Raised when RabbitMQ responds with 406 PRECONDITION_FAILED
|
|
213
228
|
class PreconditionFailed < ChannelLevelException
|
|
214
229
|
end
|
|
@@ -268,4 +283,18 @@ module Bunny
|
|
|
268
283
|
# @private
|
|
269
284
|
class MissingTLSKeyFile < Exception
|
|
270
285
|
end
|
|
286
|
+
|
|
287
|
+
# Exception wrapper that holds multiple accumulated exceptions.
|
|
288
|
+
# Raised by ExceptionAccumulator#raise_all! when multiple exceptions occurred.
|
|
289
|
+
#
|
|
290
|
+
# @api public
|
|
291
|
+
class AccumulatedExceptions < Exception
|
|
292
|
+
attr_reader :exceptions
|
|
293
|
+
|
|
294
|
+
def initialize(exceptions)
|
|
295
|
+
@exceptions = exceptions
|
|
296
|
+
messages = exceptions.map { |e| "#{e.class}: #{e.message}" }
|
|
297
|
+
super("#{exceptions.count} exception(s) accumulated:\n #{messages.join("\n ")}")
|
|
298
|
+
end
|
|
299
|
+
end
|
|
271
300
|
end
|
data/lib/bunny/exchange.rb
CHANGED
|
@@ -9,6 +9,24 @@ module Bunny
|
|
|
9
9
|
# @see http://rubybunny.info/articles/extensions.html RabbitMQ Extensions guide
|
|
10
10
|
class Exchange
|
|
11
11
|
|
|
12
|
+
#
|
|
13
|
+
# Exchange type constants
|
|
14
|
+
#
|
|
15
|
+
|
|
16
|
+
# Standard AMQP 0-9-1 exchange types
|
|
17
|
+
TYPE_DIRECT = :direct
|
|
18
|
+
TYPE_FANOUT = :fanout
|
|
19
|
+
TYPE_TOPIC = :topic
|
|
20
|
+
TYPE_HEADERS = :headers
|
|
21
|
+
|
|
22
|
+
# In RabbitMQ core since 4.3.0
|
|
23
|
+
TYPE_MODULUS_HASH = :"x-modulus-hash"
|
|
24
|
+
# In RabbitMQ core since 4.2.0
|
|
25
|
+
TYPE_LOCAL_RANDOM = :"x-local-random"
|
|
26
|
+
# Provided by commonly used plugins
|
|
27
|
+
TYPE_CONSISTENT_HASH = :"x-consistent-hash"
|
|
28
|
+
TYPE_RANDOM = :"x-random"
|
|
29
|
+
|
|
12
30
|
#
|
|
13
31
|
# API
|
|
14
32
|
#
|
|
@@ -19,7 +37,7 @@ module Bunny
|
|
|
19
37
|
# @return [String]
|
|
20
38
|
attr_reader :name
|
|
21
39
|
|
|
22
|
-
# Type of this exchange (
|
|
40
|
+
# Type of this exchange (e.g. :direct, :fanout, :topic, :headers, :"x-consistent-hash", :"x-modulus-hash").
|
|
23
41
|
# @return [Symbol]
|
|
24
42
|
attr_reader :type
|
|
25
43
|
|
|
@@ -88,7 +106,10 @@ module Bunny
|
|
|
88
106
|
|
|
89
107
|
declare! unless opts[:no_declare] || predeclared? || (@name == AMQ::Protocol::EMPTY_STRING)
|
|
90
108
|
|
|
109
|
+
# for basic.return dispatch and such
|
|
91
110
|
@channel.register_exchange(self)
|
|
111
|
+
# for topology recovery
|
|
112
|
+
@channel.record_exchange(self)
|
|
92
113
|
end
|
|
93
114
|
|
|
94
115
|
# @return [Boolean] true if this exchange was declared as durable (will survive broker restart).
|
|
@@ -140,7 +161,8 @@ module Bunny
|
|
|
140
161
|
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
|
141
162
|
# @api public
|
|
142
163
|
def publish(payload, opts = {})
|
|
143
|
-
|
|
164
|
+
rk = opts[:routing_key] || opts[:key]
|
|
165
|
+
@channel.basic_publish(payload, self.name, rk, opts)
|
|
144
166
|
|
|
145
167
|
self
|
|
146
168
|
end
|
|
@@ -155,7 +177,7 @@ module Bunny
|
|
|
155
177
|
# @see http://rubybunny.info/articles/exchanges.html Exchanges and Publishing guide
|
|
156
178
|
# @api public
|
|
157
179
|
def delete(opts = {})
|
|
158
|
-
@channel.
|
|
180
|
+
@channel.delete_recorded_exchange(self)
|
|
159
181
|
@channel.exchange_delete(@name, opts) unless predeclared?
|
|
160
182
|
end
|
|
161
183
|
|
|
@@ -220,16 +242,6 @@ module Bunny
|
|
|
220
242
|
@channel.wait_for_confirms
|
|
221
243
|
end
|
|
222
244
|
|
|
223
|
-
# @private
|
|
224
|
-
def recover_from_network_failure
|
|
225
|
-
declare! unless @options[:no_declare] ||predefined?
|
|
226
|
-
|
|
227
|
-
@bindings.each do |b|
|
|
228
|
-
bind(b[:source], b[:opts])
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
|
|
233
245
|
#
|
|
234
246
|
# Implementation
|
|
235
247
|
#
|
data/lib/bunny/get_response.rb
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "bunny/versioned_delivery_tag"
|
|
4
|
-
|
|
5
3
|
module Bunny
|
|
6
4
|
# Wraps {AMQ::Protocol::Basic::GetOk} to
|
|
7
5
|
# provide access to the delivery properties as immutable hash as
|
|
8
|
-
# well as methods.
|
|
6
|
+
# well as methods. Hash representation is created lazily.
|
|
9
7
|
class GetResponse
|
|
10
8
|
|
|
11
9
|
#
|
|
@@ -23,32 +21,38 @@ module Bunny
|
|
|
23
21
|
|
|
24
22
|
# @private
|
|
25
23
|
def initialize(get_ok, channel)
|
|
26
|
-
@get_ok
|
|
27
|
-
@
|
|
28
|
-
:delivery_tag => @get_ok.delivery_tag,
|
|
29
|
-
:redelivered => @get_ok.redelivered,
|
|
30
|
-
:exchange => @get_ok.exchange,
|
|
31
|
-
:routing_key => @get_ok.routing_key,
|
|
32
|
-
:channel => channel
|
|
33
|
-
}
|
|
34
|
-
@channel = channel
|
|
24
|
+
@get_ok = get_ok
|
|
25
|
+
@channel = channel
|
|
35
26
|
end
|
|
36
27
|
|
|
37
28
|
# Iterates over the delivery properties
|
|
38
29
|
# @see Enumerable#each
|
|
39
30
|
def each(*args, &block)
|
|
40
|
-
|
|
31
|
+
to_hash.each(*args, &block)
|
|
41
32
|
end
|
|
42
33
|
|
|
43
34
|
# Accesses delivery properties by key
|
|
44
35
|
# @see Hash#[]
|
|
45
36
|
def [](k)
|
|
46
|
-
|
|
37
|
+
case k
|
|
38
|
+
when :delivery_tag then @get_ok.delivery_tag
|
|
39
|
+
when :redelivered then @get_ok.redelivered
|
|
40
|
+
when :exchange then @get_ok.exchange
|
|
41
|
+
when :routing_key then @get_ok.routing_key
|
|
42
|
+
when :channel then @channel
|
|
43
|
+
else nil
|
|
44
|
+
end
|
|
47
45
|
end
|
|
48
46
|
|
|
49
47
|
# @return [Hash] Hash representation of this delivery info
|
|
50
48
|
def to_hash
|
|
51
|
-
@hash
|
|
49
|
+
@hash ||= {
|
|
50
|
+
:delivery_tag => @get_ok.delivery_tag,
|
|
51
|
+
:redelivered => @get_ok.redelivered,
|
|
52
|
+
:exchange => @get_ok.exchange,
|
|
53
|
+
:routing_key => @get_ok.routing_key,
|
|
54
|
+
:channel => @channel
|
|
55
|
+
}
|
|
52
56
|
end
|
|
53
57
|
|
|
54
58
|
# @private
|
|
@@ -53,10 +53,10 @@ module Bunny
|
|
|
53
53
|
sleep @interval
|
|
54
54
|
end
|
|
55
55
|
rescue IOError => ioe
|
|
56
|
-
@logger.error "I/O error in the
|
|
56
|
+
@logger.error "I/O error in the heartbeat sender: #{ioe.message}"
|
|
57
57
|
stop
|
|
58
58
|
rescue ::Exception => e
|
|
59
|
-
@logger.error "Error in the
|
|
59
|
+
@logger.error "Error in the heartbeat sender: #{e.message}"
|
|
60
60
|
stop
|
|
61
61
|
end
|
|
62
62
|
end
|
data/lib/bunny/queue.rb
CHANGED
|
@@ -17,8 +17,10 @@ module Bunny
|
|
|
17
17
|
QUORUM = "quorum"
|
|
18
18
|
CLASSIC = "classic"
|
|
19
19
|
STREAM = "stream"
|
|
20
|
+
DELAYED = "delayed"
|
|
21
|
+
JMS = "jms"
|
|
20
22
|
|
|
21
|
-
KNOWN = [CLASSIC, QUORUM, STREAM]
|
|
23
|
+
KNOWN = [CLASSIC, QUORUM, STREAM, DELAYED, JMS]
|
|
22
24
|
|
|
23
25
|
def self.known?(q_type)
|
|
24
26
|
KNOWN.include?(q_type)
|
|
@@ -28,6 +30,13 @@ module Bunny
|
|
|
28
30
|
module XArgs
|
|
29
31
|
MAX_LENGTH = "x-max-length",
|
|
30
32
|
QUEUE_TYPE = "x-queue-type"
|
|
33
|
+
|
|
34
|
+
DELAYED_RETRY_TYPE = "x-delayed-retry-type"
|
|
35
|
+
DELAYED_RETRY_MIN = "x-delayed-retry-min"
|
|
36
|
+
DELAYED_RETRY_MAX = "x-delayed-retry-max"
|
|
37
|
+
|
|
38
|
+
SELECTOR_FIELDS = "x-selector-fields"
|
|
39
|
+
SELECTOR_FIELD_MAX_BYTES = "x-selector-field-max-bytes"
|
|
31
40
|
end
|
|
32
41
|
|
|
33
42
|
# @return [Bunny::Channel] Channel this queue uses
|
|
@@ -44,7 +53,7 @@ module Bunny
|
|
|
44
53
|
# @option opts [Boolean] :durable (false) Should this queue be durable?
|
|
45
54
|
# @option opts [Boolean] :auto_delete (false) Should this queue be automatically deleted when the last consumer disconnects?
|
|
46
55
|
# @option opts [Boolean] :exclusive (false) Should this queue be exclusive (only can be used by this connection, removed when the connection is closed)?
|
|
47
|
-
# @option opts [String] :type (nil) Type of the declared queue (classic, quorum or
|
|
56
|
+
# @option opts [String] :type (nil) Type of the declared queue (classic, quorum, stream, delayed, or jms)
|
|
48
57
|
# @option opts [Hash] :arguments (nil) Additional optional arguments (typically used by RabbitMQ extensions and plugins)
|
|
49
58
|
#
|
|
50
59
|
# @see Bunny::Channel#queue
|
|
@@ -80,7 +89,10 @@ module Bunny
|
|
|
80
89
|
|
|
81
90
|
declare! unless opts[:no_declare]
|
|
82
91
|
|
|
92
|
+
# for basic.deliver dispatch and such
|
|
83
93
|
@channel.register_queue(self)
|
|
94
|
+
# for topology recovery
|
|
95
|
+
@channel.record_queue(self)
|
|
84
96
|
end
|
|
85
97
|
|
|
86
98
|
# @return [Boolean] true if this queue was declared as durable (will survive broker restart).
|
|
@@ -117,6 +129,13 @@ module Bunny
|
|
|
117
129
|
@arguments
|
|
118
130
|
end
|
|
119
131
|
|
|
132
|
+
# @param value [String]
|
|
133
|
+
# @private
|
|
134
|
+
def update_name_to(value)
|
|
135
|
+
@name = value
|
|
136
|
+
self
|
|
137
|
+
end
|
|
138
|
+
|
|
120
139
|
def to_s
|
|
121
140
|
oid = ("0x%x" % (self.object_id << 1))
|
|
122
141
|
"<#{self.class.name}:#{oid} @name=\"#{name}\" channel=#{@channel.to_s} @durable=#{@durable} @auto_delete=#{@auto_delete} @exclusive=#{@exclusive} @arguments=#{@arguments}>"
|
|
@@ -320,6 +339,7 @@ module Bunny
|
|
|
320
339
|
# @see http://rubybunny.info/articles/queues.html Queues and Consumers guide
|
|
321
340
|
# @api public
|
|
322
341
|
def delete(opts = {})
|
|
342
|
+
@channel.delete_recorded_queue_named(self.name)
|
|
323
343
|
@channel.deregister_queue(self)
|
|
324
344
|
@channel.queue_delete(@name, opts)
|
|
325
345
|
end
|
|
@@ -362,42 +382,6 @@ module Bunny
|
|
|
362
382
|
"unsupported queue type #{q_type.inspect}, supported ones: #{Types::KNOWN.join(', ')}") if (q_type and !Types.known?(q_type))
|
|
363
383
|
end
|
|
364
384
|
|
|
365
|
-
#
|
|
366
|
-
# Recovery
|
|
367
|
-
#
|
|
368
|
-
|
|
369
|
-
# @private
|
|
370
|
-
def recover_from_network_failure
|
|
371
|
-
if self.server_named?
|
|
372
|
-
old_name = @name.dup
|
|
373
|
-
@name = AMQ::Protocol::EMPTY_STRING
|
|
374
|
-
|
|
375
|
-
@channel.deregister_queue_named(old_name)
|
|
376
|
-
end
|
|
377
|
-
|
|
378
|
-
# TODO: inject and use logger
|
|
379
|
-
# puts "Recovering queue #{@name}"
|
|
380
|
-
begin
|
|
381
|
-
declare! unless @options[:no_declare]
|
|
382
|
-
|
|
383
|
-
@channel.register_queue(self)
|
|
384
|
-
rescue Exception => e
|
|
385
|
-
# TODO: inject and use logger
|
|
386
|
-
puts "Caught #{e.inspect} while redeclaring and registering #{@name}!"
|
|
387
|
-
end
|
|
388
|
-
recover_bindings
|
|
389
|
-
end
|
|
390
|
-
|
|
391
|
-
# @private
|
|
392
|
-
def recover_bindings
|
|
393
|
-
@bindings.each do |b|
|
|
394
|
-
# TODO: inject and use logger
|
|
395
|
-
# puts "Recovering binding #{b.inspect}"
|
|
396
|
-
self.bind(b[:exchange], b)
|
|
397
|
-
end
|
|
398
|
-
end
|
|
399
|
-
|
|
400
|
-
|
|
401
385
|
#
|
|
402
386
|
# Implementation
|
|
403
387
|
#
|
data/lib/bunny/reader_loop.rb
CHANGED
|
@@ -43,9 +43,13 @@ module Bunny
|
|
|
43
43
|
OpenSSL::OpenSSLError => e
|
|
44
44
|
break if terminate? || @session.closing? || @session.closed?
|
|
45
45
|
|
|
46
|
-
@
|
|
46
|
+
@mutex.synchronize do
|
|
47
|
+
@stopping = true
|
|
48
|
+
@network_is_down = true
|
|
49
|
+
end
|
|
50
|
+
|
|
47
51
|
if @session.automatically_recover?
|
|
48
|
-
log_exception(e, level: :
|
|
52
|
+
log_exception(e, level: :debug)
|
|
49
53
|
@session.handle_network_failure(e)
|
|
50
54
|
else
|
|
51
55
|
log_exception(e)
|
|
@@ -62,10 +66,6 @@ module Bunny
|
|
|
62
66
|
@network_is_down = true
|
|
63
67
|
@session_error_handler.raise(Bunny::NetworkFailure.new("caught an unexpected exception in the network loop: #{e.message}", e))
|
|
64
68
|
end
|
|
65
|
-
rescue Errno::EBADF => _ebadf
|
|
66
|
-
break if terminate?
|
|
67
|
-
# ignored, happens when we loop after the transport has already been closed
|
|
68
|
-
@mutex.synchronize { @stopping = true }
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
71
|
|