1515import unittest
1616
1717from concurrent .futures import ThreadPoolExecutor
18- from unittest .mock import Mock , ANY , call , patch
18+ from unittest .mock import Mock , ANY , call , patch , MagicMock
1919
20- from cassandra import OperationTimedOut , SchemaTargetType , SchemaChangeType
21- from cassandra .protocol import ResultMessage , RESULT_KIND_ROWS
20+ from cassandra import OperationTimedOut , SchemaTargetType , SchemaChangeType , ConsistencyLevel
21+ from cassandra .protocol import ResultMessage , RESULT_KIND_ROWS , QueryMessage
2222from cassandra .cluster import ControlConnection , _Scheduler , ProfileManager , EXEC_PROFILE_DEFAULT , ExecutionProfile
2323from cassandra .pool import Host
2424from cassandra .connection import EndPoint , DefaultEndPoint , DefaultEndPointFactory
@@ -168,6 +168,18 @@ def __init__(self):
168168 ]
169169 self .wait_for_responses = Mock (return_value = _node_meta_results (self .local_results , self .peer_results ))
170170
171+ def wait_for_response_side_effect (query_msg , timeout = None , fail_on_error = True ):
172+ result = ResultMessage (kind = RESULT_KIND_ROWS )
173+ if "peers" in query_msg .query .lower ():
174+ result .column_names = self .peer_results [0 ]
175+ result .parsed_rows = self .peer_results [1 ]
176+ else :
177+ result .column_names = self .local_results [0 ]
178+ result .parsed_rows = self .local_results [1 ]
179+ result .paging_state = None
180+ return result
181+ self .wait_for_response = Mock (side_effect = wait_for_response_side_effect )
182+
171183
172184class FakeTime (object ):
173185
@@ -312,6 +324,88 @@ def test_wait_for_schema_agreement_none_timeout(self):
312324 cc ._time = self .time
313325 assert cc ._wait_for_schema_agreement ()
314326
327+ def test_topology_queries_use_paging (self ):
328+ self .control_connection .refresh_node_list_and_token_map ()
329+ assert self .connection .wait_for_response .called
330+ calls = self .connection .wait_for_response .call_args_list
331+ for call in calls :
332+ query_msg = call [0 ][0 ]
333+ assert isinstance (query_msg , QueryMessage )
334+ assert query_msg .fetch_size == self .control_connection ._schema_meta_page_size
335+
336+ def test_topology_queries_fetch_all_pages (self ):
337+ from cassandra .cluster import _fetch_all_pages
338+ mock_connection = MagicMock ()
339+ mock_connection .endpoint = DefaultEndPoint ("192.168.1.0" )
340+ mock_connection .original_endpoint = mock_connection .endpoint
341+ first_page = ResultMessage (kind = RESULT_KIND_ROWS )
342+ first_page .column_names = ["rpc_address" , "peer" , "schema_version" , "data_center" , "rack" , "tokens" , "host_id" ]
343+ first_page .parsed_rows = [["192.168.1.1" , "10.0.0.1" , "a" , "dc1" , "rack1" , ["1" , "101" , "201" ], "uuid2" ]]
344+ first_page .paging_state = b"has_more_pages"
345+ second_page = ResultMessage (kind = RESULT_KIND_ROWS )
346+ second_page .column_names = ["rpc_address" , "peer" , "schema_version" , "data_center" , "rack" , "tokens" , "host_id" ]
347+ second_page .parsed_rows = [["192.168.1.2" , "10.0.0.2" , "a" , "dc1" , "rack1" , ["2" , "102" , "202" ], "uuid3" ]]
348+ second_page .paging_state = None
349+ mock_connection .wait_for_response .side_effect = [first_page , second_page ]
350+ self .control_connection ._connection = mock_connection
351+ query_msg = QueryMessage (query = "SELECT * FROM system.peers" ,
352+ consistency_level = ConsistencyLevel .ONE ,
353+ fetch_size = self .control_connection ._schema_meta_page_size )
354+ result = _fetch_all_pages (mock_connection , query_msg , timeout = 5 )
355+ assert len (result .parsed_rows ) == 2
356+ assert result .parsed_rows [0 ][0 ] == "192.168.1.1"
357+ assert result .parsed_rows [1 ][0 ] == "192.168.1.2"
358+ assert result .paging_state is None
359+ assert mock_connection .wait_for_response .call_count == 2
360+
361+ def test_topology_queries_fetch_all_pages_fail_on_error_false (self ):
362+ from cassandra .cluster import _fetch_all_pages
363+ mock_connection = MagicMock ()
364+ mock_connection .endpoint = DefaultEndPoint ("192.168.1.0" )
365+ mock_connection .original_endpoint = mock_connection .endpoint
366+ first_page = ResultMessage (kind = RESULT_KIND_ROWS )
367+ first_page .column_names = ["rpc_address" , "peer" , "schema_version" , "data_center" , "rack" , "tokens" , "host_id" ]
368+ first_page .parsed_rows = [["192.168.1.1" , "10.0.0.1" , "a" , "dc1" , "rack1" , ["1" , "101" , "201" ], "uuid2" ]]
369+ first_page .paging_state = b"has_more_pages"
370+ second_page = ResultMessage (kind = RESULT_KIND_ROWS )
371+ second_page .column_names = ["rpc_address" , "peer" , "schema_version" , "data_center" , "rack" , "tokens" , "host_id" ]
372+ second_page .parsed_rows = [["192.168.1.2" , "10.0.0.2" , "a" , "dc1" , "rack1" , ["2" , "102" , "202" ], "uuid3" ]]
373+ second_page .paging_state = None
374+ mock_connection .wait_for_response .side_effect = [
375+ (True , first_page ),
376+ (True , second_page ),
377+ ]
378+ query_msg = QueryMessage (query = "SELECT * FROM system.peers" ,
379+ consistency_level = ConsistencyLevel .ONE ,
380+ fetch_size = self .control_connection ._schema_meta_page_size )
381+ success , result = _fetch_all_pages (mock_connection , query_msg , timeout = 5 , fail_on_error = False )
382+ assert success
383+ assert len (result .parsed_rows ) == 2
384+ assert result .parsed_rows [0 ][0 ] == "192.168.1.1"
385+ assert result .parsed_rows [1 ][0 ] == "192.168.1.2"
386+ assert mock_connection .wait_for_response .call_count == 2
387+
388+ def test_topology_queries_fetch_all_pages_page_failure (self ):
389+ from cassandra .cluster import _fetch_all_pages
390+ mock_connection = MagicMock ()
391+ mock_connection .endpoint = DefaultEndPoint ("192.168.1.0" )
392+ mock_connection .original_endpoint = mock_connection .endpoint
393+ first_page = ResultMessage (kind = RESULT_KIND_ROWS )
394+ first_page .column_names = ["rpc_address" , "peer" , "schema_version" , "data_center" , "rack" , "tokens" , "host_id" ]
395+ first_page .parsed_rows = [["192.168.1.1" , "10.0.0.1" , "a" , "dc1" , "rack1" , ["1" , "101" , "201" ], "uuid2" ]]
396+ first_page .paging_state = b"has_more_pages"
397+ mock_connection .wait_for_response .side_effect = [
398+ (True , first_page ),
399+ (False , OperationTimedOut ()),
400+ ]
401+ query_msg = QueryMessage (query = "SELECT * FROM system.peers" ,
402+ consistency_level = ConsistencyLevel .ONE ,
403+ fetch_size = self .control_connection ._schema_meta_page_size )
404+ success , error = _fetch_all_pages (mock_connection , query_msg , timeout = 5 , fail_on_error = False )
405+ assert not success
406+ assert isinstance (error , OperationTimedOut )
407+ assert mock_connection .wait_for_response .call_count == 2
408+
315409 def test_refresh_nodes_and_tokens (self ):
316410 self .control_connection .refresh_node_list_and_token_map ()
317411 meta = self .cluster .metadata
@@ -328,7 +422,7 @@ def test_refresh_nodes_and_tokens(self):
328422 assert host .datacenter == "dc1"
329423 assert host .rack == "rack1"
330424
331- assert self .connection .wait_for_responses .call_count == 1
425+ assert self .connection .wait_for_response .call_count == 2
332426
333427 def test_refresh_nodes_and_tokens_with_invalid_peers (self ):
334428 def refresh_and_validate_added_hosts ():
@@ -444,11 +538,11 @@ def test_refresh_nodes_and_tokens_remove_host(self):
444538
445539 def test_refresh_nodes_and_tokens_timeout (self ):
446540
447- def bad_wait_for_responses (* args , ** kwargs ):
541+ def bad_wait_for_response (* args , ** kwargs ):
448542 assert kwargs ['timeout' ] == self .control_connection ._timeout
449543 raise OperationTimedOut ()
450544
451- self .connection .wait_for_responses = bad_wait_for_responses
545+ self .connection .wait_for_response = Mock ( side_effect = bad_wait_for_response )
452546 self .control_connection .refresh_node_list_and_token_map ()
453547 self .cluster .executor .submit .assert_called_with (self .control_connection ._reconnect )
454548
0 commit comments