bunny 2.23.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 +38 -36
- data/lib/amq/protocol/extensions.rb +2 -0
- data/lib/bunny/authentication/credentials_encoder.rb +2 -0
- data/lib/bunny/authentication/external_mechanism_encoder.rb +2 -0
- data/lib/bunny/authentication/plain_mechanism_encoder.rb +2 -0
- data/lib/bunny/channel.rb +778 -150
- data/lib/bunny/channel_id_allocator.rb +2 -0
- data/lib/bunny/concurrent/atomic_fixnum.rb +2 -0
- data/lib/bunny/concurrent/condition.rb +2 -0
- data/lib/bunny/concurrent/continuation_queue.rb +2 -0
- data/lib/bunny/concurrent/exception_accumulator.rb +115 -0
- data/lib/bunny/concurrent/synchronized_sorted_set.rb +2 -0
- data/lib/bunny/consumer.rb +4 -11
- data/lib/bunny/consumer_tag_generator.rb +2 -0
- data/lib/bunny/consumer_work_pool.rb +2 -0
- data/lib/bunny/cruby/socket.rb +36 -2
- data/lib/bunny/cruby/ssl_socket.rb +44 -1
- data/lib/bunny/delivery_info.rb +23 -15
- data/lib/bunny/exceptions.rb +33 -2
- data/lib/bunny/exchange.rb +27 -13
- data/lib/bunny/framing.rb +2 -0
- data/lib/bunny/get_response.rb +20 -14
- data/lib/bunny/heartbeat_sender.rb +4 -2
- data/lib/bunny/message_properties.rb +2 -0
- data/lib/bunny/queue.rb +31 -39
- data/lib/bunny/reader_loop.rb +9 -7
- data/lib/bunny/return_info.rb +18 -11
- data/lib/bunny/session.rb +387 -63
- data/lib/bunny/socket.rb +7 -12
- data/lib/bunny/ssl_socket.rb +7 -12
- data/lib/bunny/test_kit.rb +1 -0
- data/lib/bunny/timeout.rb +2 -0
- data/lib/bunny/timestamp.rb +3 -1
- data/lib/bunny/topology_recovery_filter.rb +71 -0
- data/lib/bunny/topology_registry.rb +824 -0
- data/lib/bunny/transport.rb +54 -49
- data/lib/bunny/version.rb +2 -1
- data/lib/bunny.rb +2 -1
- metadata +25 -14
- data/lib/bunny/concurrent/linked_continuation_queue.rb +0 -61
- data/lib/bunny/jruby/socket.rb +0 -57
- data/lib/bunny/jruby/ssl_socket.rb +0 -58
- data/lib/bunny/versioned_delivery_tag.rb +0 -28
data/lib/bunny/session.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "socket"
|
|
2
4
|
require "thread"
|
|
3
5
|
require "monitor"
|
|
@@ -6,15 +8,14 @@ require "bunny/transport"
|
|
|
6
8
|
require "bunny/channel_id_allocator"
|
|
7
9
|
require "bunny/heartbeat_sender"
|
|
8
10
|
require "bunny/reader_loop"
|
|
11
|
+
require "bunny/topology_registry"
|
|
12
|
+
require "bunny/topology_recovery_filter"
|
|
9
13
|
require "bunny/authentication/credentials_encoder"
|
|
10
14
|
require "bunny/authentication/plain_mechanism_encoder"
|
|
11
15
|
require "bunny/authentication/external_mechanism_encoder"
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
else
|
|
16
|
-
require "bunny/concurrent/continuation_queue"
|
|
17
|
-
end
|
|
17
|
+
require "bunny/concurrent/continuation_queue"
|
|
18
|
+
require "bunny/concurrent/exception_accumulator"
|
|
18
19
|
|
|
19
20
|
require "amq/protocol/client"
|
|
20
21
|
require "amq/settings"
|
|
@@ -62,7 +63,7 @@ module Bunny
|
|
|
62
63
|
:product => "Bunny",
|
|
63
64
|
:platform => ::RUBY_DESCRIPTION,
|
|
64
65
|
:version => Bunny::VERSION,
|
|
65
|
-
:information => "
|
|
66
|
+
:information => "https://github.com/ruby-amqp/bunny",
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
# @private
|
|
@@ -82,6 +83,8 @@ module Bunny
|
|
|
82
83
|
attr_reader :status, :heartbeat, :user, :pass, :vhost, :frame_max, :channel_max, :threaded
|
|
83
84
|
attr_reader :server_capabilities, :server_properties, :server_authentication_mechanisms, :server_locales
|
|
84
85
|
attr_reader :channel_id_allocator
|
|
86
|
+
# @return [Bunny::TopologyRegistry]
|
|
87
|
+
attr_reader :topology_registry
|
|
85
88
|
# Authentication mechanism, e.g. "PLAIN" or "EXTERNAL"
|
|
86
89
|
# @return [String]
|
|
87
90
|
attr_reader :mechanism
|
|
@@ -130,8 +133,9 @@ module Bunny
|
|
|
130
133
|
# @option connection_string_or_opts [Proc] :recovery_completed (nil) Will be called after successful connection recovery
|
|
131
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
|
|
132
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)?
|
|
133
|
-
# @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.
|
|
134
137
|
#
|
|
138
|
+
# @option connection_string_or_opts [Bunny::TopologyRecoveryFilter] :topology_recovery_filter if provided, will be used for object filtering during topology recovery
|
|
135
139
|
# @option optz [String] :auth_mechanism ("PLAIN") Authentication mechanism, PLAIN or EXTERNAL
|
|
136
140
|
# @option optz [String] :locale ("PLAIN") Locale RabbitMQ should use
|
|
137
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.
|
|
@@ -224,11 +228,14 @@ module Bunny
|
|
|
224
228
|
|
|
225
229
|
@channels = Hash.new
|
|
226
230
|
|
|
231
|
+
trf = @opts.fetch(:topology_recovery_filter, DefaultTopologyRecoveryFilter.new)
|
|
232
|
+
@topology_registry = TopologyRegistry.new(topology_recovery_filter: trf)
|
|
233
|
+
|
|
227
234
|
@recovery_attempt_started = opts[:recovery_attempt_started]
|
|
228
235
|
@recovery_completed = opts[:recovery_completed]
|
|
229
|
-
@recovery_attempts_exhausted
|
|
236
|
+
@recovery_attempts_exhausted = opts[:recovery_attempts_exhausted]
|
|
230
237
|
|
|
231
|
-
@session_error_handler = opts.fetch(:session_error_handler,
|
|
238
|
+
@session_error_handler = opts.fetch(:session_error_handler, ExceptionAccumulator.new)
|
|
232
239
|
|
|
233
240
|
@recoverable_exceptions = DEFAULT_RECOVERABLE_EXCEPTIONS.dup
|
|
234
241
|
|
|
@@ -300,7 +307,9 @@ module Bunny
|
|
|
300
307
|
def configure_socket(&block)
|
|
301
308
|
raise ArgumentError, "No block provided!" if block.nil?
|
|
302
309
|
|
|
303
|
-
@
|
|
310
|
+
@transport_mutex.synchronize do
|
|
311
|
+
@transport.configure_socket(&block)
|
|
312
|
+
end
|
|
304
313
|
end
|
|
305
314
|
|
|
306
315
|
# @return [Integer] Client socket port
|
|
@@ -326,10 +335,11 @@ module Bunny
|
|
|
326
335
|
begin
|
|
327
336
|
# close existing transport if we have one,
|
|
328
337
|
# to not leak sockets
|
|
329
|
-
@
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
338
|
+
@transport_mutex.synchronize do
|
|
339
|
+
@transport.maybe_initialize_socket
|
|
340
|
+
@transport.post_initialize_socket
|
|
341
|
+
@transport.connect
|
|
342
|
+
end
|
|
333
343
|
|
|
334
344
|
self.init_connection
|
|
335
345
|
self.open_connection
|
|
@@ -366,7 +376,7 @@ module Bunny
|
|
|
366
376
|
end
|
|
367
377
|
|
|
368
378
|
# Socket operation write timeout used by this connection
|
|
369
|
-
# @return [
|
|
379
|
+
# @return [Float]
|
|
370
380
|
# @private
|
|
371
381
|
def transport_write_timeout
|
|
372
382
|
@transport.write_timeout
|
|
@@ -386,7 +396,10 @@ module Bunny
|
|
|
386
396
|
if n && (ch = @channels[n])
|
|
387
397
|
ch
|
|
388
398
|
else
|
|
389
|
-
|
|
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
|
+
})
|
|
390
403
|
ch.open
|
|
391
404
|
ch
|
|
392
405
|
end
|
|
@@ -495,9 +508,59 @@ module Bunny
|
|
|
495
508
|
@blocked
|
|
496
509
|
end
|
|
497
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
|
+
|
|
498
561
|
# Parses an amqp[s] URI into a hash that {Bunny::Session#initialize} accepts.
|
|
499
562
|
#
|
|
500
|
-
# @param [String] uri amqp or amqps URI to parse
|
|
563
|
+
# @param [String | Hash] uri amqp or amqps URI to parse
|
|
501
564
|
# @return [Hash] Parsed URI as a hash
|
|
502
565
|
def self.parse_uri(uri)
|
|
503
566
|
AMQ::Settings.configure(uri)
|
|
@@ -516,6 +579,8 @@ module Bunny
|
|
|
516
579
|
begin
|
|
517
580
|
ch.queue(name, :passive => true)
|
|
518
581
|
true
|
|
582
|
+
rescue Bunny::ResourceLocked => _
|
|
583
|
+
true
|
|
519
584
|
rescue Bunny::NotFound => _
|
|
520
585
|
false
|
|
521
586
|
ensure
|
|
@@ -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
|
|
@@ -1076,16 +1406,7 @@ module Bunny
|
|
|
1076
1406
|
# this is the easiest way to wait until the loop
|
|
1077
1407
|
# is guaranteed to have terminated
|
|
1078
1408
|
@reader_loop.terminate_with(ShutdownSignal)
|
|
1079
|
-
|
|
1080
|
-
# on JRuby because sun.nio.ch.KQueueArrayWrapper#kevent0 is
|
|
1081
|
-
# a native method that cannot be (easily) interrupted.
|
|
1082
|
-
# So we use this ugly hack or else our test suite takes forever
|
|
1083
|
-
# to run on JRuby (a new connection is opened/closed per example). MK.
|
|
1084
|
-
if defined?(JRUBY_VERSION)
|
|
1085
|
-
sleep 0.075
|
|
1086
|
-
else
|
|
1087
|
-
@reader_loop.join
|
|
1088
|
-
end
|
|
1409
|
+
@reader_loop.join
|
|
1089
1410
|
else
|
|
1090
1411
|
# single threaded mode, nothing to do. MK.
|
|
1091
1412
|
end
|
|
@@ -1159,7 +1480,7 @@ module Bunny
|
|
|
1159
1480
|
channel.synchronize do
|
|
1160
1481
|
# see rabbitmq/rabbitmq-server#156
|
|
1161
1482
|
if open?
|
|
1162
|
-
data = frames.reduce("") { |acc, frame| acc << frame.encode }
|
|
1483
|
+
data = frames.reduce(+"") { |acc, frame| acc << frame.encode }
|
|
1163
1484
|
@transport.write(data)
|
|
1164
1485
|
signal_activity!
|
|
1165
1486
|
else
|
|
@@ -1181,7 +1502,8 @@ module Bunny
|
|
|
1181
1502
|
# locking. See a note about "single frame" methods in a comment in `send_frameset`. MK.
|
|
1182
1503
|
channel.synchronize do
|
|
1183
1504
|
if open?
|
|
1184
|
-
frames.
|
|
1505
|
+
data = frames.reduce(+"") { |acc, frame| acc << frame.encode }
|
|
1506
|
+
@transport.write_without_timeout(data)
|
|
1185
1507
|
signal_activity!
|
|
1186
1508
|
else
|
|
1187
1509
|
raise ConnectionClosedError.new(frames)
|
|
@@ -1197,10 +1519,14 @@ module Bunny
|
|
|
1197
1519
|
# If we synchronize on the channel, however, this is both thread safe and pretty fine-grained
|
|
1198
1520
|
# locking. Note that "single frame" methods do not need this kind of synchronization. MK.
|
|
1199
1521
|
channel.synchronize do
|
|
1200
|
-
|
|
1201
|
-
|
|
1522
|
+
if open?
|
|
1523
|
+
@transport.write(data)
|
|
1524
|
+
signal_activity!
|
|
1525
|
+
else
|
|
1526
|
+
raise ConnectionClosedError.new("pre-encoded data (#{data.bytesize} bytes)")
|
|
1527
|
+
end
|
|
1202
1528
|
end
|
|
1203
|
-
end #
|
|
1529
|
+
end # send_raw_without_timeout(data)
|
|
1204
1530
|
|
|
1205
1531
|
# @return [String]
|
|
1206
1532
|
# @api public
|
|
@@ -1270,7 +1596,7 @@ module Bunny
|
|
|
1270
1596
|
negotiate_value(@client_heartbeat, connection_tune.heartbeat)
|
|
1271
1597
|
end
|
|
1272
1598
|
@logger.debug { "Heartbeat interval negotiation: client = #{@client_heartbeat}, server = #{connection_tune.heartbeat}, result = #{@heartbeat}" }
|
|
1273
|
-
@logger.
|
|
1599
|
+
@logger.debug "Heartbeat interval used (in seconds): #{@heartbeat}"
|
|
1274
1600
|
|
|
1275
1601
|
# We set the read_write_timeout to twice the heartbeat value,
|
|
1276
1602
|
# and then some padding for edge cases.
|
|
@@ -1368,19 +1694,22 @@ module Bunny
|
|
|
1368
1694
|
|
|
1369
1695
|
# @private
|
|
1370
1696
|
def initialize_transport
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
@
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
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
|
|
1384
1713
|
else
|
|
1385
1714
|
raise HostListDepleted
|
|
1386
1715
|
end
|
|
@@ -1388,7 +1717,9 @@ module Bunny
|
|
|
1388
1717
|
|
|
1389
1718
|
# @private
|
|
1390
1719
|
def maybe_close_transport
|
|
1391
|
-
@
|
|
1720
|
+
@transport_mutex.synchronize do
|
|
1721
|
+
@transport.close if @transport
|
|
1722
|
+
end
|
|
1392
1723
|
end
|
|
1393
1724
|
|
|
1394
1725
|
# Sends AMQ protocol header (also known as preamble).
|
|
@@ -1402,23 +1733,16 @@ module Bunny
|
|
|
1402
1733
|
# @private
|
|
1403
1734
|
def encode_credentials(username, password)
|
|
1404
1735
|
@credentials_encoder.encode_credentials(username, password)
|
|
1405
|
-
end
|
|
1736
|
+
end
|
|
1406
1737
|
|
|
1407
1738
|
# @private
|
|
1408
1739
|
def credentials_encoder_for(mechanism)
|
|
1409
1740
|
Authentication::CredentialsEncoder.for_session(self)
|
|
1410
1741
|
end
|
|
1411
1742
|
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
@continuations = Concurrent::LinkedContinuationQueue.new
|
|
1416
|
-
end
|
|
1417
|
-
else
|
|
1418
|
-
# @private
|
|
1419
|
-
def reset_continuations
|
|
1420
|
-
@continuations = Concurrent::ContinuationQueue.new
|
|
1421
|
-
end
|
|
1743
|
+
# @private
|
|
1744
|
+
def reset_continuations
|
|
1745
|
+
@continuations = Concurrent::ContinuationQueue.new
|
|
1422
1746
|
end
|
|
1423
1747
|
|
|
1424
1748
|
# @private
|
data/lib/bunny/socket.rb
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
#
|
|
2
|
-
if defined?(JRUBY_VERSION)
|
|
3
|
-
require "bunny/jruby/socket"
|
|
1
|
+
# frozen_string_literal: true
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
SocketImpl = JRuby::Socket
|
|
7
|
-
end
|
|
8
|
-
else
|
|
9
|
-
require "bunny/cruby/socket"
|
|
3
|
+
require "bunny/cruby/socket"
|
|
10
4
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
module Bunny
|
|
6
|
+
# An alias for the standard MRI Socket,
|
|
7
|
+
# exists from the days of JRuby support.
|
|
8
|
+
SocketImpl = Socket
|
|
9
|
+
end
|