Skip to content

Commit 13bd713

Browse files
committed
control-connection: deprecate ControlConnection.wait_for_schema_agreement
Keep ControlConnection.wait_for_schema_agreement() as a compatibility wrapper, but move the existing implementation to _wait_for_schema_agreement() and deprecate the public method in favor of Session.wait_for_schema_agreement(). This lets the control-connection refresh path continue using the old logic internally without emitting warnings. The control-connection wait path was designed for internal metadata refresh use, not as a user-facing schema agreement API. It observes schema agreement from one single node, assuming that schema change statement have been ran on that host. Using it by users will lead to false positives, if user ran statement on a host different from host of control connection. Update the unit tests to call the internal helper everywhere a warning is not expected, add explicit deprecation coverage for the public wrapper, and set stacklevel=2 so the warning points at the caller instead of inside the driver.
1 parent 42a99a8 commit 13bd713

2 files changed

Lines changed: 44 additions & 9 deletions

File tree

cassandra/cluster.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4263,6 +4263,30 @@ def _handle_schema_change(self, event):
42634263
self._cluster.scheduler.schedule_unique(delay, self.refresh_schema, **event)
42644264

42654265
def wait_for_schema_agreement(self, connection=None, preloaded_results=None, wait_time=None):
4266+
"""
4267+
Wait for schema agreement from the control connection's metadata view.
4268+
4269+
This method is intended for internal metadata refresh flows. External
4270+
callers should use :meth:`.Session.wait_for_schema_agreement` instead.
4271+
4272+
The control connection observes schema agreement from its own
4273+
perspective, which may include hosts the session is not using, and it
4274+
may fail when the control connection itself is transiently unhealthy.
4275+
That can produce false positives or failures that do not reflect
4276+
whether a session can safely proceed.
4277+
4278+
.. deprecated:: 3.30.0
4279+
Use :meth:`.Session.wait_for_schema_agreement` instead.
4280+
"""
4281+
warn("ControlConnection.wait_for_schema_agreement is deprecated and will be removed in 4.0. "
4282+
"Use Session.wait_for_schema_agreement instead. "
4283+
"This method is for internal metadata refresh use only.",
4284+
DeprecationWarning, stacklevel=2)
4285+
return self._wait_for_schema_agreement(connection=connection,
4286+
preloaded_results=preloaded_results,
4287+
wait_time=wait_time)
4288+
4289+
def _wait_for_schema_agreement(self, connection=None, preloaded_results=None, wait_time=None):
42664290
total_timeout = wait_time if wait_time is not None else self._cluster.max_schema_agreement_wait
42674291
if total_timeout <= 0:
42684292
return True

tests/unit/test_control_connection.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import unittest
1616

1717
from concurrent.futures import ThreadPoolExecutor
18-
from unittest.mock import Mock, ANY, call
18+
from unittest.mock import Mock, ANY, call, patch
1919

2020
from cassandra import OperationTimedOut, SchemaTargetType, SchemaChangeType
2121
from cassandra.protocol import ResultMessage, RESULT_KIND_ROWS
@@ -210,16 +210,27 @@ def test_wait_for_schema_agreement(self):
210210
"""
211211
Basic test with all schema versions agreeing
212212
"""
213-
assert self.control_connection.wait_for_schema_agreement()
213+
assert self.control_connection._wait_for_schema_agreement()
214214
# the control connection should not have slept at all
215215
assert self.time.clock == 0
216216

217+
@patch('cassandra.cluster.warn')
218+
def test_wait_for_schema_agreement_warns_about_deprecation(self, mocked_warn):
219+
assert self.control_connection.wait_for_schema_agreement()
220+
221+
mocked_warn.assert_called_once()
222+
warning_args, warning_kwargs = mocked_warn.call_args
223+
assert 'ControlConnection.wait_for_schema_agreement is deprecated' in str(warning_args[0])
224+
assert 'Use Session.wait_for_schema_agreement instead.' in str(warning_args[0])
225+
assert warning_args[1] is DeprecationWarning
226+
assert warning_kwargs['stacklevel'] == 2
227+
217228
def test_wait_for_schema_agreement_uses_preloaded_results_if_given(self):
218229
"""
219230
wait_for_schema_agreement uses preloaded results if given for shared table queries
220231
"""
221232
preloaded_results = self._matching_schema_preloaded_results
222-
assert self.control_connection.wait_for_schema_agreement(preloaded_results=preloaded_results)
233+
assert self.control_connection._wait_for_schema_agreement(preloaded_results=preloaded_results)
223234
# the control connection should not have slept at all
224235
assert self.time.clock == 0
225236
# the connection should not have made any queries if given preloaded results
@@ -230,7 +241,7 @@ def test_wait_for_schema_agreement_falls_back_to_querying_if_schemas_dont_match_
230241
wait_for_schema_agreement requery if schema does not match using preloaded results
231242
"""
232243
preloaded_results = self._nonmatching_schema_preloaded_results
233-
assert self.control_connection.wait_for_schema_agreement(preloaded_results=preloaded_results)
244+
assert self.control_connection._wait_for_schema_agreement(preloaded_results=preloaded_results)
234245
# the control connection should not have slept at all
235246
assert self.time.clock == 0
236247
assert self.connection.wait_for_responses.call_count == 1
@@ -241,7 +252,7 @@ def test_wait_for_schema_agreement_fails(self):
241252
"""
242253
# change the schema version on one node
243254
self.connection.peer_results[1][1][2] = 'b'
244-
assert not self.control_connection.wait_for_schema_agreement()
255+
assert not self.control_connection._wait_for_schema_agreement()
245256
# the control connection should have slept until it hit the limit
246257
assert self.time.clock >= self.cluster.max_schema_agreement_wait
247258

@@ -262,7 +273,7 @@ def test_wait_for_schema_agreement_skipping(self):
262273
self.connection.peer_results[1][1][3] = 'c'
263274
self.cluster.metadata.get_host(DefaultEndPoint('192.168.1.1')).is_up = False
264275

265-
assert self.control_connection.wait_for_schema_agreement()
276+
assert self.control_connection._wait_for_schema_agreement()
266277
assert self.time.clock == 0
267278

268279
def test_wait_for_schema_agreement_rpc_lookup(self):
@@ -279,12 +290,12 @@ def test_wait_for_schema_agreement_rpc_lookup(self):
279290

280291
# even though the new host has a different schema version, it's
281292
# marked as down, so the control connection shouldn't care
282-
assert self.control_connection.wait_for_schema_agreement()
293+
assert self.control_connection._wait_for_schema_agreement()
283294
assert self.time.clock == 0
284295

285296
# but once we mark it up, the control connection will care
286297
host.is_up = True
287-
assert not self.control_connection.wait_for_schema_agreement()
298+
assert not self.control_connection._wait_for_schema_agreement()
288299
assert self.time.clock >= self.cluster.max_schema_agreement_wait
289300

290301

@@ -299,7 +310,7 @@ def test_wait_for_schema_agreement_none_timeout(self):
299310
status_event_refresh_window=0)
300311
cc._connection = self.connection
301312
cc._time = self.time
302-
assert cc.wait_for_schema_agreement()
313+
assert cc._wait_for_schema_agreement()
303314

304315
def test_refresh_nodes_and_tokens(self):
305316
self.control_connection.refresh_node_list_and_token_map()

0 commit comments

Comments
 (0)