1313# limitations under the License.
1414
1515import unittest
16+ import uuid
1617
1718from collections import deque
1819from threading import RLock
1920from unittest .mock import Mock , MagicMock , ANY
2021
2122from cassandra import ConsistencyLevel , Unavailable , SchemaTargetType , SchemaChangeType , OperationTimedOut
2223from cassandra .cluster import Session , ResponseFuture , NoHostAvailable , ProtocolVersion
23- from cassandra .connection import Connection , ConnectionException
24+ from cassandra .connection import Connection , ConnectionException , DefaultEndPoint
2425from cassandra .protocol import (ReadTimeoutErrorMessage , WriteTimeoutErrorMessage ,
2526 UnavailableErrorMessage , ResultMessage , QueryMessage ,
2627 OverloadedErrorMessage , IsBootstrappingErrorMessage ,
2728 PreparedQueryNotFound , PrepareMessage , ServerError ,
2829 RESULT_KIND_ROWS , RESULT_KIND_SET_KEYSPACE ,
2930 RESULT_KIND_SCHEMA_CHANGE , RESULT_KIND_PREPARED ,
3031 ProtocolHandler )
31- from cassandra .policies import RetryPolicy , ExponentialBackoffRetryPolicy
32- from cassandra .pool import NoConnectionsAvailable
32+ from cassandra .policies import RetryPolicy , ExponentialBackoffRetryPolicy , SimpleConvictionPolicy
33+ from cassandra .pool import Host , NoConnectionsAvailable
3334from cassandra .query import SimpleStatement
3435from tests .util import assertEqual , assertIsInstance
3536import pytest
@@ -52,7 +53,7 @@ def make_pool(self):
5253 def make_session (self ):
5354 session = self .make_basic_session ()
5455 session .cluster ._default_load_balancing_policy .make_query_plan .return_value = ['ip1' , 'ip2' ]
55- session ._pools . get .return_value = self .make_pool ()
56+ session ._get_pool_by_host_identity .return_value = self .make_pool ()
5657 return session
5758
5859 def make_response_future (self , session ):
@@ -66,7 +67,7 @@ def make_mock_response(self, col_names, rows):
6667 def test_result_message (self ):
6768 session = self .make_basic_session ()
6869 session .cluster ._default_load_balancing_policy .make_query_plan .return_value = ['ip1' , 'ip2' ]
69- pool = session ._pools . get .return_value
70+ pool = session ._get_pool_by_host_identity .return_value
7071 pool .is_shutdown = False
7172
7273 connection = Mock (spec = Connection )
@@ -75,7 +76,7 @@ def test_result_message(self):
7576 rf = self .make_response_future (session )
7677 rf .send_request ()
7778
78- rf .session ._pools . get .assert_called_once_with ('ip1' )
79+ rf .session ._get_pool_by_host_identity .assert_called_once_with ('ip1' )
7980 pool .borrow_connection .assert_called_once_with (timeout = ANY , routing_key = ANY , keyspace = ANY , table = ANY )
8081
8182 connection .send_msg .assert_called_once_with (rf .message , 1 , cb = ANY , encoder = ProtocolHandler .encode_message , decoder = ProtocolHandler .decode_message , result_metadata = [])
@@ -87,7 +88,7 @@ def test_result_message(self):
8788
8889 def test_unknown_result_class (self ):
8990 session = self .make_session ()
90- pool = session ._pools . get .return_value
91+ pool = session ._get_pool_by_host_identity .return_value
9192 connection = Mock (spec = Connection )
9293 pool .borrow_connection .return_value = (connection , 1 )
9394
@@ -151,7 +152,7 @@ def test_heartbeat_defunct_deadlock(self):
151152
152153 session = self .make_basic_session ()
153154 session .cluster ._default_load_balancing_policy .make_query_plan .return_value = [Mock (), Mock ()]
154- session ._pools . get .return_value = pool
155+ session ._get_pool_by_host_identity .return_value = pool
155156
156157 query = SimpleStatement ("SELECT * FROM foo" )
157158 message = QueryMessage (query = query , consistency_level = ConsistencyLevel .ONE )
@@ -252,7 +253,7 @@ def test_retry_policy_says_ignore(self):
252253
253254 def test_retry_policy_says_retry (self ):
254255 session = self .make_session ()
255- pool = session ._pools . get .return_value
256+ pool = session ._get_pool_by_host_identity .return_value
256257
257258 query = SimpleStatement ("INSERT INFO foo (a, b) VALUES (1, 2)" )
258259 message = QueryMessage (query = query , consistency_level = ConsistencyLevel .QUORUM )
@@ -266,7 +267,7 @@ def test_retry_policy_says_retry(self):
266267 rf = ResponseFuture (session , message , query , 1 , retry_policy = retry_policy )
267268 rf .send_request ()
268269
269- rf .session ._pools . get .assert_called_once_with ('ip1' )
270+ rf .session ._get_pool_by_host_identity .assert_called_once_with ('ip1' )
270271 pool .borrow_connection .assert_called_once_with (timeout = ANY , routing_key = ANY , keyspace = ANY , table = ANY )
271272 connection .send_msg .assert_called_once_with (rf .message , 1 , cb = ANY , encoder = ProtocolHandler .encode_message , decoder = ProtocolHandler .decode_message , result_metadata = [])
272273
@@ -285,13 +286,13 @@ def test_retry_policy_says_retry(self):
285286
286287 # it should try again with the same host since this was
287288 # an UnavailableException
288- rf .session ._pools . get .assert_called_with (host )
289+ rf .session ._get_pool_by_host_identity .assert_called_with (host )
289290 pool .borrow_connection .assert_called_with (timeout = ANY , routing_key = ANY , keyspace = ANY , table = ANY )
290291 connection .send_msg .assert_called_with (rf .message , 2 , cb = ANY , encoder = ProtocolHandler .encode_message , decoder = ProtocolHandler .decode_message , result_metadata = [])
291292
292293 def test_retry_with_different_host (self ):
293294 session = self .make_session ()
294- pool = session ._pools . get .return_value
295+ pool = session ._get_pool_by_host_identity .return_value
295296
296297 connection = Mock (spec = Connection )
297298 pool .borrow_connection .return_value = (connection , 1 )
@@ -300,7 +301,7 @@ def test_retry_with_different_host(self):
300301 rf .message .consistency_level = ConsistencyLevel .QUORUM
301302 rf .send_request ()
302303
303- rf .session ._pools . get .assert_called_once_with ('ip1' )
304+ rf .session ._get_pool_by_host_identity .assert_called_once_with ('ip1' )
304305 pool .borrow_connection .assert_called_once_with (timeout = ANY , routing_key = ANY , keyspace = ANY , table = ANY )
305306 connection .send_msg .assert_called_once_with (rf .message , 1 , cb = ANY , encoder = ProtocolHandler .encode_message , decoder = ProtocolHandler .decode_message , result_metadata = [])
306307 assert ConsistencyLevel .QUORUM == rf .message .consistency_level
@@ -319,7 +320,7 @@ def test_retry_with_different_host(self):
319320 rf ._retry_task (False , host )
320321
321322 # it should try with a different host
322- rf .session ._pools . get .assert_called_with ('ip2' )
323+ rf .session ._get_pool_by_host_identity .assert_called_with ('ip2' )
323324 pool .borrow_connection .assert_called_with (timeout = ANY , routing_key = ANY , keyspace = ANY , table = ANY )
324325 connection .send_msg .assert_called_with (rf .message , 2 , cb = ANY , encoder = ProtocolHandler .encode_message , decoder = ProtocolHandler .decode_message , result_metadata = [])
325326
@@ -328,13 +329,13 @@ def test_retry_with_different_host(self):
328329
329330 def test_all_retries_fail (self ):
330331 session = self .make_session ()
331- pool = session ._pools . get .return_value
332+ pool = session ._get_pool_by_host_identity .return_value
332333 connection = Mock (spec = Connection )
333334 pool .borrow_connection .return_value = (connection , 1 )
334335
335336 rf = self .make_response_future (session )
336337 rf .send_request ()
337- rf .session ._pools . get .assert_called_once_with ('ip1' )
338+ rf .session ._get_pool_by_host_identity .assert_called_once_with ('ip1' )
338339
339340 result = Mock (spec = IsBootstrappingErrorMessage , info = {})
340341 host = Mock ()
@@ -346,7 +347,7 @@ def test_all_retries_fail(self):
346347 rf ._retry_task (False , host )
347348
348349 # it should try with a different host
349- rf .session ._pools . get .assert_called_with ('ip2' )
350+ rf .session ._get_pool_by_host_identity .assert_called_with ('ip2' )
350351
351352 result = Mock (spec = IsBootstrappingErrorMessage , info = {})
352353 rf ._set_result (host , None , None , result )
@@ -360,15 +361,15 @@ def test_all_retries_fail(self):
360361
361362 def test_exponential_retry_policy_fail (self ):
362363 session = self .make_session ()
363- pool = session ._pools . get .return_value
364+ pool = session ._get_pool_by_host_identity .return_value
364365 connection = Mock (spec = Connection )
365366 pool .borrow_connection .return_value = (connection , 1 )
366367
367368 query = SimpleStatement ("SELECT * FROM foo" )
368369 message = QueryMessage (query = query , consistency_level = ConsistencyLevel .ONE )
369370 rf = ResponseFuture (session , message , query , 1 , retry_policy = ExponentialBackoffRetryPolicy (2 ))
370371 rf .send_request ()
371- rf .session ._pools . get .assert_called_once_with ('ip1' )
372+ rf .session ._get_pool_by_host_identity .assert_called_once_with ('ip1' )
372373
373374 result = Mock (spec = IsBootstrappingErrorMessage , info = {})
374375 host = Mock ()
@@ -384,7 +385,7 @@ def test_exponential_retry_policy_fail(self):
384385 def test_all_pools_shutdown (self ):
385386 session = self .make_basic_session ()
386387 session .cluster ._default_load_balancing_policy .make_query_plan .return_value = ['ip1' , 'ip2' ]
387- session ._pools . get .return_value .is_shutdown = True
388+ session ._get_pool_by_host_identity .return_value .is_shutdown = True
388389
389390 rf = ResponseFuture (session , Mock (), Mock (), 1 )
390391 rf .send_request ()
@@ -399,7 +400,7 @@ def test_first_pool_shutdown(self):
399400 pool_shutdown .is_shutdown = True
400401 pool_ok = self .make_pool ()
401402 pool_ok .is_shutdown = True
402- session ._pools . get .side_effect = [pool_shutdown , pool_ok ]
403+ session ._get_pool_by_host_identity .side_effect = [pool_shutdown , pool_ok ]
403404
404405 rf = self .make_response_future (session )
405406 rf .send_request ()
@@ -424,7 +425,7 @@ def test_timeout_getting_connection_from_pool(self):
424425 connection = Mock (spec = Connection )
425426 second_pool .borrow_connection .return_value = (connection , 1 )
426427
427- session ._pools . get .side_effect = [first_pool , second_pool ]
428+ session ._get_pool_by_host_identity .side_effect = [first_pool , second_pool ]
428429
429430 rf = self .make_response_future (session )
430431 rf .send_request ()
@@ -459,7 +460,7 @@ def test_callback(self):
459460
460461 def test_errback (self ):
461462 session = self .make_session ()
462- pool = session ._pools . get .return_value
463+ pool = session ._get_pool_by_host_identity .return_value
463464 connection = Mock (spec = Connection )
464465 pool .borrow_connection .return_value = (connection , 1 )
465466
@@ -508,7 +509,7 @@ def test_multiple_callbacks(self):
508509
509510 def test_multiple_errbacks (self ):
510511 session = self .make_session ()
511- pool = session ._pools . get .return_value
512+ pool = session ._get_pool_by_host_identity .return_value
512513 connection = Mock (spec = Connection )
513514 pool .borrow_connection .return_value = (connection , 1 )
514515
@@ -581,7 +582,7 @@ def test_add_callbacks(self):
581582
582583 def test_prepared_query_not_found (self ):
583584 session = self .make_session ()
584- pool = session ._pools . get .return_value
585+ pool = session ._get_pool_by_host_identity .return_value
585586 connection = Mock (spec = Connection )
586587 pool .borrow_connection .return_value = (connection , 1 )
587588
@@ -606,7 +607,7 @@ def test_prepared_query_not_found(self):
606607
607608 def test_prepared_query_not_found_bad_keyspace (self ):
608609 session = self .make_session ()
609- pool = session ._pools . get .return_value
610+ pool = session ._get_pool_by_host_identity .return_value
610611 connection = Mock (spec = Connection )
611612 pool .borrow_connection .return_value = (connection , 1 )
612613
@@ -655,7 +656,7 @@ def test_timeout_does_not_release_stream_id(self):
655656 session = self .make_basic_session ()
656657 session .cluster ._default_load_balancing_policy .make_query_plan .return_value = [Mock (endpoint = 'ip1' ), Mock (endpoint = 'ip2' )]
657658 pool = self .make_pool ()
658- session ._pools . get .return_value = pool
659+ session ._get_pool_by_host_identity .return_value = pool
659660 connection = Mock (spec = Connection , lock = RLock (), _requests = {}, request_ids = deque (),
660661 orphaned_request_ids = set (), orphaned_threshold = 256 , in_flight = 3 )
661662 pool .borrow_connection .return_value = (connection , 1 )
@@ -675,6 +676,30 @@ def test_timeout_does_not_release_stream_id(self):
675676 assert len (connection .request_ids ) == 0 , \
676677 "Request IDs should be empty but it's not: {}" .format (connection .request_ids )
677678
679+ def test_timeout_returns_orphan_to_original_pool_after_endpoint_swap (self ):
680+ session = self .make_basic_session ()
681+ host = Host (DefaultEndPoint ('127.0.0.1' ), SimpleConvictionPolicy ,
682+ host_id = uuid .uuid4 ())
683+ session .cluster ._default_load_balancing_policy .make_query_plan .return_value = [host ]
684+ old_pool = self .make_pool ()
685+ replacement_pool = self .make_pool ()
686+ connection = Mock (spec = Connection , lock = RLock (), _requests = {}, request_ids = deque (),
687+ orphaned_request_ids = set (), orphaned_threshold = 256 , in_flight = 1 )
688+ old_pool .borrow_connection .return_value = (connection , 1 )
689+ session ._get_pool_by_host_identity .side_effect = [old_pool , replacement_pool ]
690+
691+ rf = self .make_response_future (session )
692+ rf .send_request ()
693+ connection ._requests [1 ] = (connection ._handle_options_response ,
694+ ProtocolHandler .decode_message , [])
695+ host .endpoint = DefaultEndPoint ('127.0.0.2' )
696+
697+ rf ._on_timeout ()
698+
699+ replacement_pool .return_connection .assert_not_called ()
700+ old_pool .return_connection .assert_called_once_with (
701+ connection , stream_was_orphaned = True )
702+
678703 def test_single_host_query_plan_exhausted_after_one_retry (self ):
679704 """
680705 Test that when a specific host is provided, the query plan is properly
@@ -686,7 +711,7 @@ def test_single_host_query_plan_exhausted_after_one_retry(self):
686711 """
687712 session = self .make_basic_session ()
688713 pool = self .make_pool ()
689- session ._pools . get .return_value = pool
714+ session ._get_pool_by_host_identity .return_value = pool
690715
691716 # Create a specific host
692717 specific_host = Mock ()
@@ -702,7 +727,7 @@ def test_single_host_query_plan_exhausted_after_one_retry(self):
702727 rf .send_request ()
703728
704729 # Verify initial request was sent
705- rf .session ._pools . get .assert_called_once_with (specific_host )
730+ rf .session ._get_pool_by_host_identity .assert_called_once_with (specific_host )
706731 pool .borrow_connection .assert_called_once_with (timeout = ANY , routing_key = ANY , keyspace = ANY , table = ANY )
707732 connection .send_msg .assert_called_once_with (rf .message , 1 , cb = ANY , encoder = ProtocolHandler .encode_message , decoder = ProtocolHandler .decode_message , result_metadata = [])
708733
0 commit comments