Skip to content

Commit 1ffdd5c

Browse files
authored
Add BrokerConnection.connect_blocking() (#1411)
1 parent 4cbeb2e commit 1ffdd5c

File tree

5 files changed

+55
-36
lines changed

5 files changed

+55
-36
lines changed

kafka/client.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,17 +71,7 @@ def _get_conn(self, host, port, afi):
7171
)
7272

7373
conn = self._conns[host_key]
74-
conn.connect()
75-
if conn.connected():
76-
return conn
77-
78-
timeout = time.time() + self.timeout
79-
while time.time() < timeout and conn.connecting():
80-
if conn.connect() is ConnectionStates.CONNECTED:
81-
break
82-
else:
83-
time.sleep(0.05)
84-
else:
74+
if not conn.connect_blocking(self.timeout):
8575
conn.close()
8676
raise ConnectionError("%s:%s (%s)" % (host, port, afi))
8777
return conn

kafka/client_async.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -257,11 +257,7 @@ def _bootstrap(self, hosts):
257257
state_change_callback=cb,
258258
node_id='bootstrap',
259259
**self.config)
260-
bootstrap.connect()
261-
while bootstrap.connecting():
262-
self._selector.select(1)
263-
bootstrap.connect()
264-
if not bootstrap.connected():
260+
if not bootstrap.connect_blocking():
265261
bootstrap.close()
266262
continue
267263
future = bootstrap.send(metadata_request)

kafka/conn.py

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -271,18 +271,58 @@ def __init__(self, host, port, afi, **configs):
271271
self.config['metric_group_prefix'],
272272
self.node_id)
273273

274+
def _dns_lookup(self):
275+
self._gai = dns_lookup(self.host, self.port, self.afi)
276+
if not self._gai:
277+
log.error('DNS lookup failed for %s:%i (%s)',
278+
self.host, self.port, self.afi)
279+
return False
280+
return True
281+
274282
def _next_afi_host_port(self):
275283
if not self._gai:
276-
self._gai = dns_lookup(self.host, self.port, self.afi)
277-
if not self._gai:
278-
log.error('DNS lookup failed for %s:%i (%s)',
279-
self.host, self.port, self.afi)
284+
if not self._dns_lookup():
280285
return
281-
282286
afi, _, __, ___, sockaddr = self._gai.pop(0)
283287
host, port = sockaddr[:2]
284288
return (afi, host, port)
285289

290+
def connect_blocking(self, timeout=float('inf')):
291+
if self.connected():
292+
return True
293+
timeout += time.time()
294+
# First attempt to perform dns lookup
295+
# note that the underlying interface, socket.getaddrinfo,
296+
# has no explicit timeout so we may exceed the user-specified timeout
297+
while time.time() < timeout:
298+
if self._dns_lookup():
299+
break
300+
else:
301+
return False
302+
303+
# Loop once over all returned dns entries
304+
selector = None
305+
while self._gai:
306+
while time.time() < timeout:
307+
self.connect()
308+
if self.connected():
309+
if selector is not None:
310+
selector.close()
311+
return True
312+
elif self.connecting():
313+
if selector is None:
314+
selector = self.config['selector']()
315+
selector.register(self._sock, selectors.EVENT_WRITE)
316+
selector.select(1)
317+
elif self.disconnected():
318+
if selector is not None:
319+
selector.close()
320+
selector = None
321+
break
322+
else:
323+
break
324+
return False
325+
286326
def connect(self):
287327
"""Attempt to connect and return ConnectionState"""
288328
if self.state is ConnectionStates.DISCONNECTED and not self.blacked_out():
@@ -903,19 +943,9 @@ def filter(self, record):
903943
((0, 8, 0), MetadataRequest[0]([])),
904944
]
905945

906-
def connect():
907-
self.connect()
908-
if self.connected():
909-
return
910-
timeout_at = time.time() + timeout
911-
while time.time() < timeout_at and self.connecting():
912-
if self.connect() is ConnectionStates.CONNECTED:
913-
return
914-
time.sleep(0.05)
915-
raise Errors.NodeNotReadyError()
916-
917946
for version, request in test_cases:
918-
connect()
947+
if not self.connect_blocking(timeout):
948+
raise Errors.NodeNotReadyError()
919949
f = self.send(request)
920950
# HACK: sleeping to wait for socket to send bytes
921951
time.sleep(0.1)

test/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ def _set_conn_state(state):
128128
return state
129129
conn._set_conn_state = _set_conn_state
130130
conn.connect.side_effect = lambda: conn.state
131+
conn.connect_blocking.return_value = True
131132
conn.connecting = lambda: conn.state in (ConnectionStates.CONNECTING,
132133
ConnectionStates.HANDSHAKE)
133134
conn.connected = lambda: conn.state is ConnectionStates.CONNECTED

test/test_client_async.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,22 @@ def test_bootstrap_success(conn):
5555
kwargs.pop('state_change_callback')
5656
kwargs.pop('node_id')
5757
assert kwargs == cli.config
58-
conn.connect.assert_called_with()
58+
conn.connect_blocking.assert_called_with()
5959
conn.send.assert_called_once_with(MetadataRequest[0]([]))
6060
assert cli._bootstrap_fails == 0
6161
assert cli.cluster.brokers() == set([BrokerMetadata(0, 'foo', 12, None),
6262
BrokerMetadata(1, 'bar', 34, None)])
6363

64+
6465
def test_bootstrap_failure(conn):
65-
conn.state = ConnectionStates.DISCONNECTED
66+
conn.connect_blocking.return_value = False
6667
cli = KafkaClient(api_version=(0, 9))
6768
args, kwargs = conn.call_args
6869
assert args == ('localhost', 9092, socket.AF_UNSPEC)
6970
kwargs.pop('state_change_callback')
7071
kwargs.pop('node_id')
7172
assert kwargs == cli.config
72-
conn.connect.assert_called_with()
73+
conn.connect_blocking.assert_called_with()
7374
conn.close.assert_called_with()
7475
assert cli._bootstrap_fails == 1
7576
assert cli.cluster.brokers() == set()
@@ -95,6 +96,7 @@ def test_can_connect(cli, conn):
9596
conn.blacked_out.return_value = True
9697
assert not cli._can_connect(0)
9798

99+
98100
def test_maybe_connect(cli, conn):
99101
try:
100102
# Node not in metadata, raises AssertionError

0 commit comments

Comments
 (0)