Skip to content

Commit 67778d8

Browse files
committed
fix: use reactor-aware executor factory for control-connection paging, fix mock contract and lint issue
- ControlConnection._try_connect and _refresh_node_list_and_token_map now build their paging ThreadPoolExecutor via Cluster._create_thread_pool_executor instead of instantiating concurrent.futures.ThreadPoolExecutor directly. The bare executor bypassed the Eventlet detection that factory exists for, reintroducing the known Python 3.7+/Eventlet hang for users configured with EventletConnection. GreenThreadPoolExecutor (futurist) subclasses concurrent.futures._base.Executor, so it supports the same `with ... as executor:` context-manager protocol, no restructuring needed. - tests/unit/test_control_connection.py: MockConnection.wait_for_response now honors the fail_on_error=False contract, returning (True, result) instead of a bare result, matching cassandra/connection.py's Connection.fetch_all_pages/wait_for_response behavior. Added a regression test covering this. Also added MockCluster._create_thread_pool_executor so tests exercising the fixed call sites keep working. - Renamed a loop variable that shadowed the `call` import from unittest.mock (Ruff F402) in test_topology_queries_use_paging.
1 parent 6bdf2fb commit 67778d8

2 files changed

Lines changed: 22 additions & 5 deletions

File tree

cassandra/cluster.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3935,7 +3935,7 @@ def _try_connect(self, endpoint):
39353935
consistency_level=ConsistencyLevel.ONE,
39363936
fetch_size=self._schema_meta_page_size)
39373937

3938-
with ThreadPoolExecutor(max_workers=2) as executor:
3938+
with self._cluster._create_thread_pool_executor(max_workers=2) as executor:
39393939
local_future = executor.submit(
39403940
connection.fetch_all_pages, local_query, self._timeout, False)
39413941
peers_future = executor.submit(
@@ -4103,7 +4103,7 @@ def _refresh_node_list_and_token_map(self, connection, preloaded_results=None,
41034103
consistency_level=cl,
41044104
fetch_size=self._schema_meta_page_size)
41054105

4106-
with ThreadPoolExecutor(max_workers=2) as executor:
4106+
with self._cluster._create_thread_pool_executor(max_workers=2) as executor:
41074107
peers_future = executor.submit(
41084108
connection.fetch_all_pages, peers_query, self._timeout)
41094109
local_future = executor.submit(

tests/unit/test_control_connection.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ def add_host(self, endpoint, datacenter, rack, signal=False, refresh_nodes=True,
121121
def remove_host(self, host):
122122
pass
123123

124+
def _create_thread_pool_executor(self, **kwargs):
125+
return ThreadPoolExecutor(**kwargs)
126+
124127
def on_up(self, host):
125128
pass
126129

@@ -177,7 +180,7 @@ def wait_for_response_side_effect(query_msg, timeout=None, fail_on_error=True):
177180
result.column_names = self.local_results[0]
178181
result.parsed_rows = self.local_results[1]
179182
result.paging_state = None
180-
return result
183+
return result if fail_on_error else (True, result)
181184
self.wait_for_response = Mock(side_effect=wait_for_response_side_effect)
182185

183186
def fetch_all_pages(self, query_msg, timeout, fail_on_error=True):
@@ -331,11 +334,25 @@ def test_topology_queries_use_paging(self):
331334
self.control_connection.refresh_node_list_and_token_map()
332335
assert self.connection.wait_for_response.called
333336
calls = self.connection.wait_for_response.call_args_list
334-
for call in calls:
335-
query_msg = call[0][0]
337+
for response_call in calls:
338+
query_msg = response_call[0][0]
336339
assert isinstance(query_msg, QueryMessage)
337340
assert query_msg.fetch_size == self.control_connection._schema_meta_page_size
338341

342+
def test_mock_connection_fetch_all_pages_honors_fail_on_error_false(self):
343+
"""
344+
Regression test for MockConnection.fetch_all_pages/wait_for_response:
345+
when fail_on_error=False, it must return (True, result), matching
346+
the real Connection.fetch_all_pages/wait_for_response contract used
347+
by ControlConnection._try_connect (which passes fail_on_error=False
348+
and unpacks the result as a (success, result) tuple).
349+
"""
350+
query_msg = QueryMessage(query="SELECT * FROM system.local",
351+
consistency_level=ConsistencyLevel.ONE)
352+
success, result = self.connection.fetch_all_pages(query_msg, timeout=5, fail_on_error=False)
353+
assert success is True
354+
assert isinstance(result, ResultMessage)
355+
339356
def test_topology_queries_fetch_all_pages(self):
340357
from cassandra.connection import Connection as RealConnection
341358
mock_connection = MagicMock()

0 commit comments

Comments
 (0)