@@ -303,7 +303,7 @@ def _can_connect(self, node_id):
303
303
304
304
def _conn_state_change (self , node_id , sock , conn ):
305
305
with self ._lock :
306
- if conn .connecting () :
306
+ if conn .state is ConnectionStates . CONNECTING :
307
307
# SSL connections can enter this state 2x (second during Handshake)
308
308
if node_id not in self ._connecting :
309
309
self ._connecting .add (node_id )
@@ -315,7 +315,19 @@ def _conn_state_change(self, node_id, sock, conn):
315
315
if self .cluster .is_bootstrap (node_id ):
316
316
self ._last_bootstrap = time .time ()
317
317
318
- elif conn .connected ():
318
+ elif conn .state is ConnectionStates .API_VERSIONS_SEND :
319
+ try :
320
+ self ._selector .register (sock , selectors .EVENT_WRITE , conn )
321
+ except KeyError :
322
+ self ._selector .modify (sock , selectors .EVENT_WRITE , conn )
323
+
324
+ elif conn .state in (ConnectionStates .API_VERSIONS_RECV , ConnectionStates .AUTHENTICATING ):
325
+ try :
326
+ self ._selector .register (sock , selectors .EVENT_READ , conn )
327
+ except KeyError :
328
+ self ._selector .modify (sock , selectors .EVENT_READ , conn )
329
+
330
+ elif conn .state is ConnectionStates .CONNECTED :
319
331
log .debug ("Node %s connected" , node_id )
320
332
if node_id in self ._connecting :
321
333
self ._connecting .remove (node_id )
@@ -332,6 +344,8 @@ def _conn_state_change(self, node_id, sock, conn):
332
344
333
345
if self .cluster .is_bootstrap (node_id ):
334
346
self ._bootstrap_fails = 0
347
+ if self ._api_versions is None :
348
+ self ._api_versions = conn ._api_versions
335
349
336
350
else :
337
351
for node_id in list (self ._conns .keys ()):
@@ -970,15 +984,14 @@ def refresh_done(val_or_error):
970
984
def get_api_versions (self ):
971
985
"""Return the ApiVersions map, if available.
972
986
973
- Note: A call to check_version must previously have succeeded and returned
974
- version 0.10.0 or later
987
+ Note: Only available after bootstrap; requires broker version 0.10.0 or later.
975
988
976
989
Returns: a map of dict mapping {api_key : (min_version, max_version)},
977
990
or None if ApiVersion is not supported by the kafka cluster.
978
991
"""
979
992
return self ._api_versions
980
993
981
- def check_version (self , node_id = None , timeout = None , strict = False ):
994
+ def check_version (self , node_id = None , timeout = None , ** kwargs ):
982
995
"""Attempt to guess the version of a Kafka broker.
983
996
984
997
Keyword Arguments:
@@ -994,50 +1007,45 @@ def check_version(self, node_id=None, timeout=None, strict=False):
994
1007
Raises:
995
1008
NodeNotReadyError (if node_id is provided)
996
1009
NoBrokersAvailable (if node_id is None)
997
- UnrecognizedBrokerVersion: please file bug if seen!
998
- AssertionError (if strict=True): please file bug if seen!
999
1010
"""
1000
1011
timeout = timeout or (self .config ['api_version_auto_timeout_ms' ] / 1000 )
1001
- self ._lock .acquire ()
1002
- end = time .time () + timeout
1003
- while time .time () < end :
1004
-
1005
- # It is possible that least_loaded_node falls back to bootstrap,
1006
- # which can block for an increasing backoff period
1007
- try_node = node_id or self .least_loaded_node ()
1008
- if try_node is None :
1009
- self ._lock .release ()
1010
- raise Errors .NoBrokersAvailable ()
1011
- if not self ._init_connect (try_node ):
1012
- if try_node == node_id :
1013
- raise Errors .NodeNotReadyError ("Connection failed to %s" % node_id )
1014
- else :
1012
+ with self ._lock :
1013
+ end = time .time () + timeout
1014
+ while time .time () < end :
1015
+ time_remaining = max (end - time .time (), 0 )
1016
+ if node_id is not None and self .connection_delay (node_id ) > 0 :
1017
+ sleep_time = min (time_remaining , self .connection_delay (node_id ) / 1000.0 )
1018
+ if sleep_time > 0 :
1019
+ time .sleep (sleep_time )
1015
1020
continue
1016
-
1017
- conn = self ._conns [try_node ]
1018
-
1019
- # We will intentionally cause socket failures
1020
- # These should not trigger metadata refresh
1021
- self ._refresh_on_disconnects = False
1022
- try :
1023
- remaining = end - time .time ()
1024
- version = conn .check_version (timeout = remaining , strict = strict , topics = list (self .config ['bootstrap_topics_filter' ]))
1025
- if not self ._api_versions :
1026
- self ._api_versions = conn .get_api_versions ()
1027
- self ._lock .release ()
1028
- return version
1029
- except Errors .NodeNotReadyError :
1030
- # Only raise to user if this is a node-specific request
1021
+ try_node = node_id or self .least_loaded_node ()
1022
+ if try_node is None :
1023
+ sleep_time = min (time_remaining , self .least_loaded_node_refresh_ms () / 1000.0 )
1024
+ if sleep_time > 0 :
1025
+ log .warning ('No node available during check_version; sleeping %.2f secs' , sleep_time )
1026
+ time .sleep (sleep_time )
1027
+ continue
1028
+ log .debug ('Attempting to check version with node %s' , try_node )
1029
+ if not self ._init_connect (try_node ):
1030
+ if try_node == node_id :
1031
+ raise Errors .NodeNotReadyError ("Connection failed to %s" % node_id )
1032
+ else :
1033
+ continue
1034
+ conn = self ._conns [try_node ]
1035
+
1036
+ while conn .connecting () and time .time () < end :
1037
+ timeout_ms = min ((end - time .time ()) * 1000 , 200 )
1038
+ self .poll (timeout_ms = timeout_ms )
1039
+
1040
+ if conn ._api_version is not None :
1041
+ return conn ._api_version
1042
+
1043
+ # Timeout
1044
+ else :
1031
1045
if node_id is not None :
1032
- self ._lock .release ()
1033
- raise
1034
- finally :
1035
- self ._refresh_on_disconnects = True
1036
-
1037
- # Timeout
1038
- else :
1039
- self ._lock .release ()
1040
- raise Errors .NoBrokersAvailable ()
1046
+ raise Errors .NodeNotReadyError (node_id )
1047
+ else :
1048
+ raise Errors .NoBrokersAvailable ()
1041
1049
1042
1050
def api_version (self , operation , max_version = None ):
1043
1051
"""Find the latest version of the protocol operation supported by both
@@ -1063,15 +1071,15 @@ def api_version(self, operation, max_version=None):
1063
1071
broker_api_versions = self ._api_versions
1064
1072
api_key = operation [0 ].API_KEY
1065
1073
if broker_api_versions is None or api_key not in broker_api_versions :
1066
- raise IncompatibleBrokerVersion (
1074
+ raise Errors . IncompatibleBrokerVersion (
1067
1075
"Kafka broker does not support the '{}' Kafka protocol."
1068
1076
.format (operation [0 ].__name__ ))
1069
1077
broker_min_version , broker_max_version = broker_api_versions [api_key ]
1070
1078
version = min (max_version , broker_max_version )
1071
1079
if version < broker_min_version :
1072
1080
# max library version is less than min broker version. Currently,
1073
1081
# no Kafka versions specify a min msg version. Maybe in the future?
1074
- raise IncompatibleBrokerVersion (
1082
+ raise Errors . IncompatibleBrokerVersion (
1075
1083
"No version of the '{}' Kafka protocol is supported by both the client and broker."
1076
1084
.format (operation [0 ].__name__ ))
1077
1085
return version
0 commit comments