bunny 2.24.0 → 3.0.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 +12 -4
- data/lib/bunny/channel.rb +709 -123
- 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 +376 -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 +28 -6
- data/lib/bunny/versioned_delivery_tag.rb +0 -30
data/lib/bunny/return_info.rb
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Bunny
|
|
4
4
|
# Wraps AMQ::Protocol::Basic::Return to
|
|
5
|
-
# provide access to the
|
|
6
|
-
# well as methods.
|
|
5
|
+
# provide access to the return properties as immutable hash as
|
|
6
|
+
# well as methods. Hash representation is created lazily.
|
|
7
7
|
class ReturnInfo
|
|
8
8
|
|
|
9
9
|
#
|
|
@@ -18,29 +18,34 @@ module Bunny
|
|
|
18
18
|
|
|
19
19
|
def initialize(basic_return)
|
|
20
20
|
@basic_return = basic_return
|
|
21
|
-
@hash = {
|
|
22
|
-
:reply_code => basic_return.reply_code,
|
|
23
|
-
:reply_text => basic_return.reply_text,
|
|
24
|
-
:exchange => basic_return.exchange,
|
|
25
|
-
:routing_key => basic_return.routing_key
|
|
26
|
-
}
|
|
27
21
|
end
|
|
28
22
|
|
|
29
23
|
# Iterates over the returned delivery properties
|
|
30
24
|
# @see Enumerable#each
|
|
31
25
|
def each(*args, &block)
|
|
32
|
-
|
|
26
|
+
to_hash.each(*args, &block)
|
|
33
27
|
end
|
|
34
28
|
|
|
35
29
|
# Accesses returned delivery properties by key
|
|
36
30
|
# @see Hash#[]
|
|
37
31
|
def [](k)
|
|
38
|
-
|
|
32
|
+
case k
|
|
33
|
+
when :reply_code then @basic_return.reply_code
|
|
34
|
+
when :reply_text then @basic_return.reply_text
|
|
35
|
+
when :exchange then @basic_return.exchange
|
|
36
|
+
when :routing_key then @basic_return.routing_key
|
|
37
|
+
else nil
|
|
38
|
+
end
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
# @return [Hash] Hash representation of this returned delivery info
|
|
42
42
|
def to_hash
|
|
43
|
-
@hash
|
|
43
|
+
@hash ||= {
|
|
44
|
+
:reply_code => @basic_return.reply_code,
|
|
45
|
+
:reply_text => @basic_return.reply_text,
|
|
46
|
+
:exchange => @basic_return.exchange,
|
|
47
|
+
:routing_key => @basic_return.routing_key
|
|
48
|
+
}
|
|
44
49
|
end
|
|
45
50
|
|
|
46
51
|
# @private
|
data/lib/bunny/session.rb
CHANGED
|
@@ -8,11 +8,14 @@ require "bunny/transport"
|
|
|
8
8
|
require "bunny/channel_id_allocator"
|
|
9
9
|
require "bunny/heartbeat_sender"
|
|
10
10
|
require "bunny/reader_loop"
|
|
11
|
+
require "bunny/topology_registry"
|
|
12
|
+
require "bunny/topology_recovery_filter"
|
|
11
13
|
require "bunny/authentication/credentials_encoder"
|
|
12
14
|
require "bunny/authentication/plain_mechanism_encoder"
|
|
13
15
|
require "bunny/authentication/external_mechanism_encoder"
|
|
14
16
|
|
|
15
17
|
require "bunny/concurrent/continuation_queue"
|
|
18
|
+
require "bunny/concurrent/exception_accumulator"
|
|
16
19
|
|
|
17
20
|
require "amq/protocol/client"
|
|
18
21
|
require "amq/settings"
|
|
@@ -60,7 +63,7 @@ module Bunny
|
|
|
60
63
|
:product => "Bunny",
|
|
61
64
|
:platform => ::RUBY_DESCRIPTION,
|
|
62
65
|
:version => Bunny::VERSION,
|
|
63
|
-
:information => "
|
|
66
|
+
:information => "https://github.com/ruby-amqp/bunny",
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
# @private
|
|
@@ -80,6 +83,8 @@ module Bunny
|
|
|
80
83
|
attr_reader :status, :heartbeat, :user, :pass, :vhost, :frame_max, :channel_max, :threaded
|
|
81
84
|
attr_reader :server_capabilities, :server_properties, :server_authentication_mechanisms, :server_locales
|
|
82
85
|
attr_reader :channel_id_allocator
|
|
86
|
+
# @return [Bunny::TopologyRegistry]
|
|
87
|
+
attr_reader :topology_registry
|
|
83
88
|
# Authentication mechanism, e.g. "PLAIN" or "EXTERNAL"
|
|
84
89
|
# @return [String]
|
|
85
90
|
attr_reader :mechanism
|
|
@@ -128,8 +133,9 @@ module Bunny
|
|
|
128
133
|
# @option connection_string_or_opts [Proc] :recovery_completed (nil) Will be called after successful connection recovery
|
|
129
134
|
# @option connection_string_or_opts [Proc] :recovery_attempts_exhausted (nil) Will be called when the connection recovery failed after the specified amount of recovery attempts
|
|
130
135
|
# @option connection_string_or_opts [Boolean] :recover_from_connection_close (true) Should this connection recover after receiving a server-sent connection.close (e.g. connection was force closed)?
|
|
131
|
-
# @option connection_string_or_opts [Object] :session_error_handler (
|
|
136
|
+
# @option connection_string_or_opts [Object] :session_error_handler (ExceptionAccumulator.new) Object which responds to #raise that will act as a session error handler. Defaults to a Bunny::ExceptionAccumulator instance which stores exceptions for safe retrieval later. Can be set to Thread.current for legacy behavior (raises asynchronous exceptions in the creating thread), or any object that responds to #raise.
|
|
132
137
|
#
|
|
138
|
+
# @option connection_string_or_opts [Bunny::TopologyRecoveryFilter] :topology_recovery_filter if provided, will be used for object filtering during topology recovery
|
|
133
139
|
# @option optz [String] :auth_mechanism ("PLAIN") Authentication mechanism, PLAIN or EXTERNAL
|
|
134
140
|
# @option optz [String] :locale ("PLAIN") Locale RabbitMQ should use
|
|
135
141
|
# @option optz [String] :connection_name (nil) Client-provided connection name, if any. Note that the value returned does not uniquely identify a connection and cannot be used as a connection identifier in HTTP API requests.
|
|
@@ -222,11 +228,14 @@ module Bunny
|
|
|
222
228
|
|
|
223
229
|
@channels = Hash.new
|
|
224
230
|
|
|
231
|
+
trf = @opts.fetch(:topology_recovery_filter, DefaultTopologyRecoveryFilter.new)
|
|
232
|
+
@topology_registry = TopologyRegistry.new(topology_recovery_filter: trf)
|
|
233
|
+
|
|
225
234
|
@recovery_attempt_started = opts[:recovery_attempt_started]
|
|
226
235
|
@recovery_completed = opts[:recovery_completed]
|
|
227
|
-
@recovery_attempts_exhausted
|
|
236
|
+
@recovery_attempts_exhausted = opts[:recovery_attempts_exhausted]
|
|
228
237
|
|
|
229
|
-
@session_error_handler = opts.fetch(:session_error_handler,
|
|
238
|
+
@session_error_handler = opts.fetch(:session_error_handler, ExceptionAccumulator.new)
|
|
230
239
|
|
|
231
240
|
@recoverable_exceptions = DEFAULT_RECOVERABLE_EXCEPTIONS.dup
|
|
232
241
|
|
|
@@ -298,7 +307,9 @@ module Bunny
|
|
|
298
307
|
def configure_socket(&block)
|
|
299
308
|
raise ArgumentError, "No block provided!" if block.nil?
|
|
300
309
|
|
|
301
|
-
@
|
|
310
|
+
@transport_mutex.synchronize do
|
|
311
|
+
@transport.configure_socket(&block)
|
|
312
|
+
end
|
|
302
313
|
end
|
|
303
314
|
|
|
304
315
|
# @return [Integer] Client socket port
|
|
@@ -324,10 +335,11 @@ module Bunny
|
|
|
324
335
|
begin
|
|
325
336
|
# close existing transport if we have one,
|
|
326
337
|
# to not leak sockets
|
|
327
|
-
@
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
338
|
+
@transport_mutex.synchronize do
|
|
339
|
+
@transport.maybe_initialize_socket
|
|
340
|
+
@transport.post_initialize_socket
|
|
341
|
+
@transport.connect
|
|
342
|
+
end
|
|
331
343
|
|
|
332
344
|
self.init_connection
|
|
333
345
|
self.open_connection
|
|
@@ -364,7 +376,7 @@ module Bunny
|
|
|
364
376
|
end
|
|
365
377
|
|
|
366
378
|
# Socket operation write timeout used by this connection
|
|
367
|
-
# @return [
|
|
379
|
+
# @return [Float]
|
|
368
380
|
# @private
|
|
369
381
|
def transport_write_timeout
|
|
370
382
|
@transport.write_timeout
|
|
@@ -384,7 +396,10 @@ module Bunny
|
|
|
384
396
|
if n && (ch = @channels[n])
|
|
385
397
|
ch
|
|
386
398
|
else
|
|
387
|
-
|
|
399
|
+
work_pool = ConsumerWorkPool.new(consumer_pool_size || 1, consumer_pool_abort_on_exception, consumer_pool_shutdown_timeout)
|
|
400
|
+
ch = Bunny::Channel.new(self, n, {
|
|
401
|
+
work_pool: work_pool
|
|
402
|
+
})
|
|
388
403
|
ch.open
|
|
389
404
|
ch
|
|
390
405
|
end
|
|
@@ -493,9 +508,59 @@ module Bunny
|
|
|
493
508
|
@blocked
|
|
494
509
|
end
|
|
495
510
|
|
|
511
|
+
# Returns the session error handler.
|
|
512
|
+
# By default, this is a Bunny::ExceptionAccumulator instance.
|
|
513
|
+
#
|
|
514
|
+
# @return [Object] the session error handler
|
|
515
|
+
# @api public
|
|
516
|
+
attr_reader :session_error_handler
|
|
517
|
+
|
|
518
|
+
# Returns true if any exceptions have been accumulated by the session error handler.
|
|
519
|
+
# Only works when using the default ExceptionAccumulator handler.
|
|
520
|
+
#
|
|
521
|
+
# @return [Boolean] true if exceptions have been accumulated
|
|
522
|
+
# @raise [NoMethodError] if the session error handler doesn't respond to #any?
|
|
523
|
+
# @api public
|
|
524
|
+
def exception_occurred?
|
|
525
|
+
@session_error_handler.any?
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
# Returns all accumulated exceptions from the session error handler.
|
|
529
|
+
# Only works when using the default ExceptionAccumulator handler.
|
|
530
|
+
#
|
|
531
|
+
# @return [Array<Exception>] all accumulated exceptions
|
|
532
|
+
# @raise [NoMethodError] if the session error handler doesn't respond to #all
|
|
533
|
+
# @api public
|
|
534
|
+
def exceptions
|
|
535
|
+
@session_error_handler.all
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
# Clears all accumulated exceptions from the session error handler.
|
|
539
|
+
# Only works when using the default ExceptionAccumulator handler.
|
|
540
|
+
#
|
|
541
|
+
# @return [Array<Exception>] the exceptions that were cleared
|
|
542
|
+
# @raise [NoMethodError] if the session error handler doesn't respond to #clear
|
|
543
|
+
# @api public
|
|
544
|
+
def clear_exceptions
|
|
545
|
+
@session_error_handler.clear
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
# Raises the first accumulated exception if any exist.
|
|
549
|
+
# Only works when using the default ExceptionAccumulator handler.
|
|
550
|
+
#
|
|
551
|
+
# This is useful for checking errors at safe points in your application,
|
|
552
|
+
# such as after completing a batch of operations.
|
|
553
|
+
#
|
|
554
|
+
# @raise [Exception] the first accumulated exception
|
|
555
|
+
# @raise [NoMethodError] if the session error handler doesn't respond to #raise_first!
|
|
556
|
+
# @api public
|
|
557
|
+
def raise_on_exception!
|
|
558
|
+
@session_error_handler.raise_first!
|
|
559
|
+
end
|
|
560
|
+
|
|
496
561
|
# Parses an amqp[s] URI into a hash that {Bunny::Session#initialize} accepts.
|
|
497
562
|
#
|
|
498
|
-
# @param [String] uri amqp or amqps URI to parse
|
|
563
|
+
# @param [String | Hash] uri amqp or amqps URI to parse
|
|
499
564
|
# @return [Hash] Parsed URI as a hash
|
|
500
565
|
def self.parse_uri(uri)
|
|
501
566
|
AMQ::Settings.configure(uri)
|
|
@@ -562,6 +627,17 @@ module Bunny
|
|
|
562
627
|
@recovery_attempts_exhausted = block
|
|
563
628
|
end
|
|
564
629
|
|
|
630
|
+
# Recovers topology (exchanges, queues, bindings, consumers) for a single channel.
|
|
631
|
+
# Intended for use after {Bunny::Channel#reopen} to restore the channel to a
|
|
632
|
+
# working state with its original consumers.
|
|
633
|
+
#
|
|
634
|
+
# @param [Bunny::Channel] ch Channel whose topology should be recovered
|
|
635
|
+
# @api public
|
|
636
|
+
def recover_channel_topology(ch)
|
|
637
|
+
filter = Proc.new { |entity| entity.channel == ch }
|
|
638
|
+
recover_topology_with(filter)
|
|
639
|
+
end
|
|
640
|
+
|
|
565
641
|
#
|
|
566
642
|
# Implementation
|
|
567
643
|
#
|
|
@@ -747,7 +823,9 @@ module Bunny
|
|
|
747
823
|
@reader_loop.stop if @reader_loop
|
|
748
824
|
maybe_shutdown_heartbeat_sender
|
|
749
825
|
|
|
750
|
-
|
|
826
|
+
recover_connection_and_channels
|
|
827
|
+
recover_topology
|
|
828
|
+
notify_of_recovery_completion
|
|
751
829
|
else
|
|
752
830
|
@logger.error "Exception #{exception.message} is considered unrecoverable..."
|
|
753
831
|
end
|
|
@@ -757,16 +835,135 @@ module Bunny
|
|
|
757
835
|
end
|
|
758
836
|
end
|
|
759
837
|
|
|
838
|
+
# @return [Boolean]
|
|
760
839
|
# @private
|
|
761
840
|
def recoverable_network_failure?(exception)
|
|
762
841
|
@recoverable_exceptions.any? {|x| exception.kind_of? x}
|
|
763
842
|
end
|
|
764
843
|
|
|
844
|
+
# @return [Boolean]
|
|
765
845
|
# @private
|
|
766
846
|
def recovering_from_network_failure?
|
|
767
847
|
@recovering_from_network_failure
|
|
768
848
|
end
|
|
769
849
|
|
|
850
|
+
# @param [Bunny::Queue] queue
|
|
851
|
+
# @private
|
|
852
|
+
def record_queue(queue)
|
|
853
|
+
@topology_registry.record_queue(queue)
|
|
854
|
+
end
|
|
855
|
+
|
|
856
|
+
# @param [Bunny::Channel] ch
|
|
857
|
+
# @param [String] name
|
|
858
|
+
# @param [Boolean] server_named
|
|
859
|
+
# @param [Boolean] durable
|
|
860
|
+
# @param [Boolean] auto_delete
|
|
861
|
+
# @param [Boolean] exclusive
|
|
862
|
+
# @param [Hash] arguments
|
|
863
|
+
def record_queue_with(ch, name, server_named, durable, auto_delete, exclusive, arguments)
|
|
864
|
+
@topology_registry.record_queue_with(ch, name, server_named, durable, auto_delete, exclusive, arguments)
|
|
865
|
+
end
|
|
866
|
+
|
|
867
|
+
# @param [Bunny::Queue, Bunny::RecordedQueue] queue
|
|
868
|
+
# @private
|
|
869
|
+
def delete_recoreded_queue(queue)
|
|
870
|
+
@topology_registry.delete_recorded_queue(queue)
|
|
871
|
+
end
|
|
872
|
+
|
|
873
|
+
# @param [String] name
|
|
874
|
+
# @private
|
|
875
|
+
def delete_recorded_queue_named(name)
|
|
876
|
+
@topology_registry.delete_recorded_queue_named(name)
|
|
877
|
+
end
|
|
878
|
+
|
|
879
|
+
# @param [Bunny::Exchange] exchange
|
|
880
|
+
# @private
|
|
881
|
+
def record_exchange(exchange)
|
|
882
|
+
@topology_registry.record_exchange(exchange)
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
# @param [Bunny::Channel] ch
|
|
886
|
+
# @param [String] name
|
|
887
|
+
# @param [String] type
|
|
888
|
+
# @param [Boolean] durable
|
|
889
|
+
# @param [Boolean] auto_delete
|
|
890
|
+
# @param [Hash] arguments
|
|
891
|
+
def record_exchange_with(ch, name, type, durable, auto_delete, arguments)
|
|
892
|
+
@topology_registry.record_exchange_with(ch, name, type, durable, auto_delete, arguments)
|
|
893
|
+
end
|
|
894
|
+
|
|
895
|
+
# @param [Bunny::Exchange] exchange
|
|
896
|
+
# @private
|
|
897
|
+
def delete_recorded_exchange(exchange)
|
|
898
|
+
@topology_registry.delete_recorded_exchange(exchange)
|
|
899
|
+
end
|
|
900
|
+
|
|
901
|
+
# @param [String] name
|
|
902
|
+
# @private
|
|
903
|
+
def delete_recorded_exchange_named(name)
|
|
904
|
+
@topology_registry.delete_recorded_exchange_named(name)
|
|
905
|
+
end
|
|
906
|
+
|
|
907
|
+
# @param [Bunny::Channel] ch
|
|
908
|
+
# @param [String] exchange_name
|
|
909
|
+
# @param [String] queue_name
|
|
910
|
+
# @param [String] routing_key
|
|
911
|
+
# @param [Hash] arguments
|
|
912
|
+
# @private
|
|
913
|
+
def record_queue_binding_with(ch, exchange_name, queue_name, routing_key, arguments)
|
|
914
|
+
@topology_registry.record_queue_binding_with(ch, exchange_name, queue_name, routing_key, arguments)
|
|
915
|
+
end
|
|
916
|
+
|
|
917
|
+
# @param [Bunny::Channel] ch
|
|
918
|
+
# @param [String] exchange_name
|
|
919
|
+
# @param [String] queue_name
|
|
920
|
+
# @param [String] routing_key
|
|
921
|
+
# @param [Hash] arguments
|
|
922
|
+
# @private
|
|
923
|
+
def delete_recorded_queue_binding(ch, exchange_name, queue_name, routing_key, arguments)
|
|
924
|
+
@topology_registry.delete_recorded_queue_binding(ch, exchange_name, queue_name, routing_key, arguments)
|
|
925
|
+
end
|
|
926
|
+
|
|
927
|
+
# @param [Bunny::Channel] ch
|
|
928
|
+
# @param [String] source_name
|
|
929
|
+
# @param [String] destination_name
|
|
930
|
+
# @param [String] routing_key
|
|
931
|
+
# @param [Hash] arguments
|
|
932
|
+
# @private
|
|
933
|
+
def record_exchange_binding_with(ch, source_name, destination_name, routing_key, arguments)
|
|
934
|
+
@topology_registry.record_exchange_binding_with(ch, source_name, destination_name, routing_key, arguments)
|
|
935
|
+
end
|
|
936
|
+
|
|
937
|
+
# @param [Bunny::Channel] ch
|
|
938
|
+
# @param [String] source_name
|
|
939
|
+
# @param [String] destination_name
|
|
940
|
+
# @param [String] routing_key
|
|
941
|
+
# @param [Hash] arguments
|
|
942
|
+
# @private
|
|
943
|
+
def delete_recorded_exchange_binding(ch, source_name, destination_name, routing_key, arguments)
|
|
944
|
+
@topology_registry.delete_recorded_exchange_binding(ch, source_name, destination_name, routing_key, arguments)
|
|
945
|
+
end
|
|
946
|
+
|
|
947
|
+
# @param [Bunny::Channel] ch
|
|
948
|
+
# @param [String] consumer_tag
|
|
949
|
+
# @param [String] queue_name
|
|
950
|
+
# @param [#call] callable
|
|
951
|
+
# @param [Boolean] manual_ack
|
|
952
|
+
# @param [Boolean] exclusive
|
|
953
|
+
# @param [Hash] arguments
|
|
954
|
+
# @private
|
|
955
|
+
def record_consumer_with(ch, consumer_tag, queue_name, callable, manual_ack, exclusive, arguments)
|
|
956
|
+
@topology_registry.record_consumer_with(ch, consumer_tag, queue_name, callable, manual_ack, exclusive, arguments)
|
|
957
|
+
end
|
|
958
|
+
|
|
959
|
+
# @param [String] consumer_tag
|
|
960
|
+
# @private
|
|
961
|
+
def delete_recorded_consumer(consumer_tag)
|
|
962
|
+
@topology_registry.delete_recorded_consumer(consumer_tag)
|
|
963
|
+
end
|
|
964
|
+
|
|
965
|
+
|
|
966
|
+
|
|
770
967
|
# @private
|
|
771
968
|
def announce_network_failure_recovery
|
|
772
969
|
if recovery_attempts_limited?
|
|
@@ -777,18 +974,17 @@ module Bunny
|
|
|
777
974
|
end
|
|
778
975
|
|
|
779
976
|
# @private
|
|
780
|
-
def
|
|
977
|
+
def recover_connection_and_channels
|
|
781
978
|
sleep @network_recovery_interval
|
|
782
979
|
@logger.debug "Will attempt connection recovery..."
|
|
783
980
|
notify_of_recovery_attempt_start
|
|
784
981
|
|
|
785
982
|
self.initialize_transport
|
|
786
983
|
|
|
787
|
-
@logger.
|
|
984
|
+
@logger.debug "Retrying connection on next host in line: #{@transport.host}:#{@transport.port}"
|
|
788
985
|
self.start
|
|
789
986
|
|
|
790
987
|
if open?
|
|
791
|
-
|
|
792
988
|
@recovering_from_network_failure = false
|
|
793
989
|
@logger.debug "Connection is now open"
|
|
794
990
|
if @reset_recovery_attempt_counter_after_reconnection
|
|
@@ -799,7 +995,6 @@ module Bunny
|
|
|
799
995
|
end
|
|
800
996
|
|
|
801
997
|
recover_channels
|
|
802
|
-
notify_of_recovery_completion
|
|
803
998
|
end
|
|
804
999
|
rescue HostListDepleted
|
|
805
1000
|
reset_address_index
|
|
@@ -858,6 +1053,141 @@ module Bunny
|
|
|
858
1053
|
end
|
|
859
1054
|
end
|
|
860
1055
|
|
|
1056
|
+
# @private
|
|
1057
|
+
def recover_topology
|
|
1058
|
+
@logger.debug "Will recover topology now"
|
|
1059
|
+
recover_topology_with
|
|
1060
|
+
end
|
|
1061
|
+
|
|
1062
|
+
# @private
|
|
1063
|
+
def recover_topology_with(filter = nil)
|
|
1064
|
+
exchanges = @topology_registry.filtered_exchanges.reject(&:predeclared?)
|
|
1065
|
+
queues = @topology_registry.filtered_queues
|
|
1066
|
+
queue_bindings = @topology_registry.filtered_queue_bindings
|
|
1067
|
+
exchange_bindings = @topology_registry.filtered_exchange_bindings
|
|
1068
|
+
consumers = @topology_registry.filtered_consumers
|
|
1069
|
+
|
|
1070
|
+
if filter
|
|
1071
|
+
exchanges = exchanges.select(&filter)
|
|
1072
|
+
queues = queues.select(&filter)
|
|
1073
|
+
queue_bindings = queue_bindings.select(&filter)
|
|
1074
|
+
exchange_bindings = exchange_bindings.select(&filter)
|
|
1075
|
+
consumers = consumers.select(&filter)
|
|
1076
|
+
end
|
|
1077
|
+
|
|
1078
|
+
@logger.debug { "Will recover #{exchanges.size} exchange(s)" }
|
|
1079
|
+
exchanges.each do |x|
|
|
1080
|
+
begin
|
|
1081
|
+
recover_exchange(x)
|
|
1082
|
+
rescue Exception => e
|
|
1083
|
+
@logger.error "Caught an exception while recovering exchange #{x.name}: #{e.inspect}"
|
|
1084
|
+
end
|
|
1085
|
+
end
|
|
1086
|
+
|
|
1087
|
+
@logger.debug { "Will recover #{queues.size} queue(s)" }
|
|
1088
|
+
queues.each do |q|
|
|
1089
|
+
begin
|
|
1090
|
+
recover_queue(q)
|
|
1091
|
+
rescue Exception => e
|
|
1092
|
+
@logger.error "Caught an exception while recovering queue #{q.name}: #{e.inspect}"
|
|
1093
|
+
end
|
|
1094
|
+
end
|
|
1095
|
+
|
|
1096
|
+
@logger.debug { "Will recover #{queue_bindings.size + exchange_bindings.size} binding(s)" }
|
|
1097
|
+
queue_bindings.each do |b|
|
|
1098
|
+
begin
|
|
1099
|
+
recover_queue_binding(b)
|
|
1100
|
+
rescue Exception => e
|
|
1101
|
+
@logger.error "Caught an exception while recovering a binding of queue #{b.destination}: #{e.inspect}"
|
|
1102
|
+
end
|
|
1103
|
+
end
|
|
1104
|
+
|
|
1105
|
+
exchange_bindings.each do |b|
|
|
1106
|
+
begin
|
|
1107
|
+
recover_exchange_binding(b)
|
|
1108
|
+
rescue Exception => e
|
|
1109
|
+
@logger.error "Caught an exception while recovering a binding of exchange #{b.source}: #{e.inspect}"
|
|
1110
|
+
end
|
|
1111
|
+
end
|
|
1112
|
+
|
|
1113
|
+
@logger.debug { "Will recover #{consumers.size} consumer(s)" }
|
|
1114
|
+
consumers.each do |c|
|
|
1115
|
+
recover_consumer(c)
|
|
1116
|
+
end
|
|
1117
|
+
end
|
|
1118
|
+
|
|
1119
|
+
# @param [Bunny::RecordedExchange] x
|
|
1120
|
+
# @private
|
|
1121
|
+
def recover_exchange(x)
|
|
1122
|
+
opts = {
|
|
1123
|
+
durable: x.durable,
|
|
1124
|
+
auto_delete: x.auto_delete,
|
|
1125
|
+
arguments: x.arguments
|
|
1126
|
+
}
|
|
1127
|
+
x.channel.exchange_declare(x.name, x.type, opts)
|
|
1128
|
+
end
|
|
1129
|
+
|
|
1130
|
+
# @param [Bunny::RecordedQueue] q
|
|
1131
|
+
# @private
|
|
1132
|
+
def recover_queue(q)
|
|
1133
|
+
opts = {
|
|
1134
|
+
durable: q.durable,
|
|
1135
|
+
auto_delete: q.auto_delete,
|
|
1136
|
+
exclusive: q.exclusive,
|
|
1137
|
+
arguments: q.arguments
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
old_name = q.name
|
|
1141
|
+
# this response carries the server-generated name
|
|
1142
|
+
queue_declare_ok = q.channel.queue_declare(q.name_to_use_for_recovery, opts)
|
|
1143
|
+
new_name = queue_declare_ok.queue
|
|
1144
|
+
|
|
1145
|
+
# if the name has changed, update all the bindings where
|
|
1146
|
+
# this queue is the destination, then all consumers
|
|
1147
|
+
if new_name != old_name
|
|
1148
|
+
record_queue_name_change(old_name, new_name)
|
|
1149
|
+
q.channel.record_queue_name_change(old_name, new_name)
|
|
1150
|
+
end
|
|
1151
|
+
end
|
|
1152
|
+
|
|
1153
|
+
# @param [String] old_name
|
|
1154
|
+
# @param [String] new_name
|
|
1155
|
+
# @private
|
|
1156
|
+
def record_queue_name_change(old_name, new_name)
|
|
1157
|
+
@topology_registry.record_queue_name_change(old_name, new_name)
|
|
1158
|
+
end
|
|
1159
|
+
|
|
1160
|
+
# @param [Bunny::RecordedQueueBinding] rb
|
|
1161
|
+
# @private
|
|
1162
|
+
def recover_queue_binding(rb)
|
|
1163
|
+
opts = {
|
|
1164
|
+
routing_key: rb.routing_key,
|
|
1165
|
+
arguments: rb.arguments
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
rb.channel.queue_bind_without_recording_topology(rb.destination, rb.source, opts)
|
|
1169
|
+
end
|
|
1170
|
+
|
|
1171
|
+
# @param [Bunny::RecordedExchangeBindingBinding] rb
|
|
1172
|
+
# @private
|
|
1173
|
+
def recover_exchange_binding(rb)
|
|
1174
|
+
opts = {
|
|
1175
|
+
routing_key: rb.routing_key,
|
|
1176
|
+
arguments: rb.arguments
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
rb.channel.exchange_bind_without_recording_topology(rb.source, rb.destination, opts)
|
|
1180
|
+
end
|
|
1181
|
+
|
|
1182
|
+
# @param [Bunny::RecordedConsumer] c
|
|
1183
|
+
# @private
|
|
1184
|
+
def recover_consumer(c)
|
|
1185
|
+
c.channel.maybe_reinitialize_consumer_pool!
|
|
1186
|
+
c.channel.basic_consume(c.queue_name, c.consumer_tag, !c.manual_ack, c.exclusive, c.arguments) do |*args|
|
|
1187
|
+
c.callable.call(*args)
|
|
1188
|
+
end
|
|
1189
|
+
end
|
|
1190
|
+
|
|
861
1191
|
# @private
|
|
862
1192
|
def notify_of_recovery_attempt_start
|
|
863
1193
|
@recovery_attempt_started.call if @recovery_attempt_started
|
|
@@ -1172,7 +1502,8 @@ module Bunny
|
|
|
1172
1502
|
# locking. See a note about "single frame" methods in a comment in `send_frameset`. MK.
|
|
1173
1503
|
channel.synchronize do
|
|
1174
1504
|
if open?
|
|
1175
|
-
frames.
|
|
1505
|
+
data = frames.reduce(+"") { |acc, frame| acc << frame.encode }
|
|
1506
|
+
@transport.write_without_timeout(data)
|
|
1176
1507
|
signal_activity!
|
|
1177
1508
|
else
|
|
1178
1509
|
raise ConnectionClosedError.new(frames)
|
|
@@ -1188,10 +1519,14 @@ module Bunny
|
|
|
1188
1519
|
# If we synchronize on the channel, however, this is both thread safe and pretty fine-grained
|
|
1189
1520
|
# locking. Note that "single frame" methods do not need this kind of synchronization. MK.
|
|
1190
1521
|
channel.synchronize do
|
|
1191
|
-
|
|
1192
|
-
|
|
1522
|
+
if open?
|
|
1523
|
+
@transport.write(data)
|
|
1524
|
+
signal_activity!
|
|
1525
|
+
else
|
|
1526
|
+
raise ConnectionClosedError.new("pre-encoded data (#{data.bytesize} bytes)")
|
|
1527
|
+
end
|
|
1193
1528
|
end
|
|
1194
|
-
end #
|
|
1529
|
+
end # send_raw_without_timeout(data)
|
|
1195
1530
|
|
|
1196
1531
|
# @return [String]
|
|
1197
1532
|
# @api public
|
|
@@ -1359,19 +1694,22 @@ module Bunny
|
|
|
1359
1694
|
|
|
1360
1695
|
# @private
|
|
1361
1696
|
def initialize_transport
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
@
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1697
|
+
address = @addresses[@address_index]
|
|
1698
|
+
if address
|
|
1699
|
+
@transport_mutex.synchronize do
|
|
1700
|
+
@address_index_mutex.synchronize { @address_index += 1 }
|
|
1701
|
+
@transport.close rescue nil # Let's make sure the previous transport socket is closed
|
|
1702
|
+
@transport = Transport.new(self,
|
|
1703
|
+
host_from_address(address),
|
|
1704
|
+
port_from_address(address),
|
|
1705
|
+
@opts.merge(:session_error_handler => @session_error_handler)
|
|
1706
|
+
)
|
|
1707
|
+
|
|
1708
|
+
# Reset the cached progname for the logger only when no logger was provided
|
|
1709
|
+
@default_logger.progname = self.to_s
|
|
1710
|
+
|
|
1711
|
+
@transport
|
|
1712
|
+
end
|
|
1375
1713
|
else
|
|
1376
1714
|
raise HostListDepleted
|
|
1377
1715
|
end
|
|
@@ -1379,7 +1717,9 @@ module Bunny
|
|
|
1379
1717
|
|
|
1380
1718
|
# @private
|
|
1381
1719
|
def maybe_close_transport
|
|
1382
|
-
@
|
|
1720
|
+
@transport_mutex.synchronize do
|
|
1721
|
+
@transport.close if @transport
|
|
1722
|
+
end
|
|
1383
1723
|
end
|
|
1384
1724
|
|
|
1385
1725
|
# Sends AMQ protocol header (also known as preamble).
|
|
@@ -1393,7 +1733,7 @@ module Bunny
|
|
|
1393
1733
|
# @private
|
|
1394
1734
|
def encode_credentials(username, password)
|
|
1395
1735
|
@credentials_encoder.encode_credentials(username, password)
|
|
1396
|
-
end
|
|
1736
|
+
end
|
|
1397
1737
|
|
|
1398
1738
|
# @private
|
|
1399
1739
|
def credentials_encoder_for(mechanism)
|