@@ -12,6 +12,7 @@ class Node
1212 SLOT_SIZE = 16_384
1313 MIN_SLOT = 0
1414 MAX_SLOT = SLOT_SIZE - 1
15+ MAX_STARTUP_SAMPLE = 37
1516 IGNORE_GENERIC_CONFIG_KEYS = %i[ url host port path ] . freeze
1617
1718 ReloadNeeded = Class . new ( ::RedisClient ::Error )
@@ -32,19 +33,33 @@ def build_connection_prelude
3233 end
3334
3435 class << self
35- def load_info ( options , **kwargs )
36- startup_nodes = ::RedisClient ::Cluster ::Node . new ( options , **kwargs )
37-
38- errors = startup_nodes . map do |n |
39- reply = n . call ( 'CLUSTER' , 'NODES' )
40- return parse_node_info ( reply )
41- rescue ::RedisClient ::ConnectionError , ::RedisClient ::CommandError => e
42- e
36+ def load_info ( options , **kwargs ) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
37+ startup_size = options . size > MAX_STARTUP_SAMPLE ? MAX_STARTUP_SAMPLE : options . size
38+ node_info_list = Array . new ( startup_size )
39+ errors = Array . new ( startup_size )
40+ startup_options = options . to_a . sample ( MAX_STARTUP_SAMPLE ) . to_h
41+ startup_nodes = ::RedisClient ::Cluster ::Node . new ( startup_options , **kwargs )
42+ threads = startup_nodes . each_with_index . map do |raw_client , idx |
43+ Thread . new ( raw_client , idx ) do |cli , i |
44+ Thread . pass
45+ reply = cli . call ( 'CLUSTER' , 'NODES' )
46+ node_info_list [ i ] = parse_node_info ( reply )
47+ rescue StandardError => e
48+ errors [ i ] = e
49+ ensure
50+ cli &.close
51+ end
52+ end
53+ threads . each ( &:join )
54+ raise ::RedisClient ::Cluster ::InitialSetupError , errors if node_info_list . all? ( &:nil? )
55+
56+ grouped = node_info_list . compact . group_by do |rows |
57+ rows . sort_by { |row | row [ :id ] }
58+ . map { |r | "#{ r [ :id ] } #{ r [ :node_key ] } #{ r [ :role ] } #{ r [ :primary_id ] } #{ r [ :config_epoch ] } " }
59+ . join
4360 end
4461
45- raise ::RedisClient ::Cluster ::InitialSetupError , errors
46- ensure
47- startup_nodes &.each ( &:close )
62+ grouped . max_by { |_ , v | v . size } [ 1 ] . first
4863 end
4964
5065 private
@@ -109,32 +124,58 @@ def replica_node_keys
109124 end
110125
111126 def find_by ( node_key )
127+ raise ReloadNeeded if node_key . nil? || !@clients . key? ( node_key )
128+
112129 @clients . fetch ( node_key )
113- rescue KeyError
114- raise ReloadNeeded
115130 end
116131
117132 def call_all ( method , *command , **kwargs , &block )
118- try_map { |_ , client | client . send ( method , *command , **kwargs , &block ) } . values
133+ results , errors = try_map do |_ , client |
134+ client . send ( method , *command , **kwargs , &block )
135+ end
136+
137+ return results . values if errors . empty?
138+
139+ raise ::RedisClient ::Cluster ::ErrorCollection , errors
119140 end
120141
121142 def call_primaries ( method , *command , **kwargs , &block )
122- try_map do |node_key , client |
143+ results , errors = try_map do |node_key , client |
123144 next if replica? ( node_key )
124145
125146 client . send ( method , *command , **kwargs , &block )
126- end . values
147+ end
148+
149+ return results . values if errors . empty?
150+
151+ raise ::RedisClient ::Cluster ::ErrorCollection , errors
127152 end
128153
129154 def call_replicas ( method , *command , **kwargs , &block )
130155 return call_primaries ( method , *command , **kwargs , &block ) if replica_disabled?
131156
132157 replica_node_keys = @replications . values . map ( &:sample )
133- try_map do |node_key , client |
158+ results , errors = try_map do |node_key , client |
134159 next if primary? ( node_key ) || !replica_node_keys . include? ( node_key )
135160
136161 client . send ( method , *command , **kwargs , &block )
137- end . values
162+ end
163+
164+ return results . values if errors . empty?
165+
166+ raise ::RedisClient ::Cluster ::ErrorCollection , errors
167+ end
168+
169+ def send_ping ( method , *command , **kwargs , &block )
170+ results , errors = try_map do |_ , client |
171+ client . send ( method , *command , **kwargs , &block )
172+ end
173+
174+ return results . values if errors . empty?
175+
176+ raise ReloadNeeded if errors . values . any? ( ::RedisClient ::ConnectionError )
177+
178+ raise ::RedisClient ::Cluster ::ErrorCollection , errors
138179 end
139180
140181 def scale_reading_clients
@@ -144,21 +185,18 @@ def scale_reading_clients
144185 end
145186 end
146187
147- def slot_exists? ( slot )
148- slot = Integer ( slot )
149- return false if slot < MIN_SLOT || slot > MAX_SLOT
150-
151- !@slots [ slot ] . nil?
152- end
153-
154188 def find_node_key_of_primary ( slot )
189+ return if slot . nil?
190+
155191 slot = Integer ( slot )
156192 return if slot < MIN_SLOT || slot > MAX_SLOT
157193
158194 @slots [ slot ]
159195 end
160196
161197 def find_node_key_of_replica ( slot )
198+ return if slot . nil?
199+
162200 slot = Integer ( slot )
163201 return if slot < MIN_SLOT || slot > MAX_SLOT
164202
@@ -241,9 +279,7 @@ def try_map # rubocop:disable Metrics/MethodLength
241279 end
242280
243281 threads . each ( &:join )
244- return results if errors . empty?
245-
246- raise ::RedisClient ::Cluster ::ErrorCollection , errors
282+ [ results , errors ]
247283 end
248284 end
249285 end
0 commit comments