1313from azure .cosmos import CosmosClient
1414from azure .cosmos .exceptions import CosmosHttpResponseError
1515from _fault_injection_transport import FaultInjectionTransport
16- from test_per_partition_circuit_breaker_mm_async import DELETE , CREATE , UPSERT , REPLACE , PATCH , BATCH , validate_response_uri , READ , \
16+ from test_per_partition_circuit_breaker_mm_async import DELETE , CREATE , UPSERT , REPLACE , PATCH , BATCH , \
17+ validate_response_uri , READ , \
1718 QUERY_PK , QUERY , CHANGE_FEED , CHANGE_FEED_PK , CHANGE_FEED_EPK , READ_ALL_ITEMS , REGION_1 , REGION_2 , \
1819 write_operations_and_errors , validate_unhealthy_partitions , read_operations_and_errors , PK_VALUE , operations , \
19- create_doc
20+ create_doc , validate_stats
2021from test_per_partition_circuit_breaker_mm_async import DELETE_ALL_ITEMS_BY_PARTITION_KEY
2122
2223def perform_write_operation (operation , container , fault_injection_container , doc_id , pk , expected_uri ):
@@ -99,15 +100,18 @@ class TestPerPartitionCircuitBreakerMM:
99100 host = test_config .TestConfig .host
100101 master_key = test_config .TestConfig .masterKey
101102 TEST_DATABASE_ID = test_config .TestConfig .TEST_DATABASE_ID
102- TEST_CONTAINER_SINGLE_PARTITION_ID = test_config .TestConfig .TEST_MULTI_PARTITION_CONTAINER_ID
103+ TEST_CONTAINER_MULTI_PARTITION_ID = test_config .TestConfig .TEST_MULTI_PARTITION_CONTAINER_ID
103104
104105 def setup_method_with_custom_transport (self , custom_transport , default_endpoint = host , ** kwargs ):
106+ container_id = kwargs .pop ("container_id" , None )
107+ if not container_id :
108+ container_id = self .TEST_CONTAINER_MULTI_PARTITION_ID
105109 client = CosmosClient (default_endpoint , self .master_key ,
106110 preferred_locations = [REGION_1 , REGION_2 ],
107111 multiple_write_locations = True ,
108112 transport = custom_transport , ** kwargs )
109113 db = client .get_database_client (self .TEST_DATABASE_ID )
110- container = db .get_container_client (self . TEST_CONTAINER_SINGLE_PARTITION_ID )
114+ container = db .get_container_client (container_id )
111115 return {"client" : client , "db" : db , "col" : container }
112116
113117 @pytest .mark .parametrize ("write_operation, error" , write_operations_and_errors ())
@@ -151,8 +155,8 @@ def test_write_consecutive_failure_threshold(self, write_operation, error):
151155
152156 validate_unhealthy_partitions (global_endpoint_manager , 1 )
153157 # remove faults and reduce initial recover time and perform a write
154- original_unavailable_time = _partition_health_tracker .INITIAL_UNAVAILABLE_TIME
155- _partition_health_tracker .INITIAL_UNAVAILABLE_TIME = 1
158+ original_unavailable_time = _partition_health_tracker .INITIAL_UNAVAILABLE_TIME_MS
159+ _partition_health_tracker .INITIAL_UNAVAILABLE_TIME_MS = 1
156160 custom_transport .faults = []
157161 try :
158162 perform_write_operation (write_operation ,
@@ -162,7 +166,7 @@ def test_write_consecutive_failure_threshold(self, write_operation, error):
162166 PK_VALUE ,
163167 uri_down )
164168 finally :
165- _partition_health_tracker .INITIAL_UNAVAILABLE_TIME = original_unavailable_time
169+ _partition_health_tracker .INITIAL_UNAVAILABLE_TIME_MS = original_unavailable_time
166170 validate_unhealthy_partitions (global_endpoint_manager , 0 )
167171
168172 @pytest .mark .cosmosCircuitBreakerMultiRegion
@@ -203,8 +207,8 @@ def test_read_consecutive_failure_threshold(self, read_operation, error):
203207 expected_unhealthy_partitions = 1
204208 validate_unhealthy_partitions (global_endpoint_manager , expected_unhealthy_partitions )
205209 # remove faults and reduce initial recover time and perform a read
206- original_unavailable_time = _partition_health_tracker .INITIAL_UNAVAILABLE_TIME
207- _partition_health_tracker .INITIAL_UNAVAILABLE_TIME = 1
210+ original_unavailable_time = _partition_health_tracker .INITIAL_UNAVAILABLE_TIME_MS
211+ _partition_health_tracker .INITIAL_UNAVAILABLE_TIME_MS = 1
208212 custom_transport .faults = []
209213 try :
210214 perform_read_operation (read_operation ,
@@ -213,7 +217,7 @@ def test_read_consecutive_failure_threshold(self, read_operation, error):
213217 doc ['pk' ],
214218 uri_down )
215219 finally :
216- _partition_health_tracker .INITIAL_UNAVAILABLE_TIME = original_unavailable_time
220+ _partition_health_tracker .INITIAL_UNAVAILABLE_TIME_MS = original_unavailable_time
217221 validate_unhealthy_partitions (global_endpoint_manager , 0 )
218222
219223 @pytest .mark .parametrize ("write_operation, error" , write_operations_and_errors ())
@@ -297,7 +301,7 @@ def test_read_failure_rate_threshold(self, read_operation, error):
297301 # restore minimum requests
298302 _partition_health_tracker .MINIMUM_REQUESTS_FOR_FAILURE_RATE = 100
299303
300- def setup_info (self , error ):
304+ def setup_info (self , error , ** kwargs ):
301305 expected_uri = _location_cache .LocationCache .GetLocationalEndpoint (self .host , REGION_2 )
302306 uri_down = _location_cache .LocationCache .GetLocationalEndpoint (self .host , REGION_1 )
303307 custom_transport = FaultInjectionTransport ()
@@ -307,12 +311,57 @@ def setup_info(self, error):
307311 FaultInjectionTransport .predicate_targets_region (r , uri_down ))
308312 custom_transport .add_fault (predicate ,
309313 error )
310- custom_setup = self .setup_method_with_custom_transport (custom_transport , default_endpoint = self .host )
314+ custom_setup = self .setup_method_with_custom_transport (custom_transport , default_endpoint = self .host , ** kwargs )
311315 fault_injection_container = custom_setup ['col' ]
312- setup = self .setup_method_with_custom_transport (None , default_endpoint = self .host )
316+ setup = self .setup_method_with_custom_transport (None , default_endpoint = self .host , ** kwargs )
313317 container = setup ['col' ]
314318 return container , doc , expected_uri , uri_down , fault_injection_container , custom_transport , predicate
315319
320+ def test_stat_reset (self ):
321+ error_lambda = lambda r : FaultInjectionTransport .error_after_delay (
322+ 0 ,
323+ CosmosHttpResponseError (
324+ status_code = 503 ,
325+ message = "Some injected error." )
326+ )
327+ container , doc , expected_uri , uri_down , fault_injection_container , custom_transport , predicate = \
328+ self .setup_info (error_lambda , container_id = test_config .TestConfig .TEST_SINGLE_PARTITION_CONTAINER_ID )
329+ container .upsert_item (body = doc )
330+ sleep (1 )
331+ global_endpoint_manager = fault_injection_container .client_connection ._global_endpoint_manager
332+ # lower refresh interval for testing
333+ _partition_health_tracker .REFRESH_INTERVAL_MS = 10 * 1000
334+ try :
335+ for i in range (2 ):
336+ validate_unhealthy_partitions (global_endpoint_manager , 0 )
337+ # read will fail and retry in other region
338+ perform_read_operation (READ ,
339+ fault_injection_container ,
340+ doc ['id' ],
341+ PK_VALUE ,
342+ expected_uri )
343+ try :
344+ perform_write_operation (CREATE ,
345+ container ,
346+ fault_injection_container ,
347+ str (uuid .uuid4 ()),
348+ PK_VALUE ,
349+ expected_uri )
350+ except CosmosHttpResponseError as e :
351+ assert e .status_code == 503
352+ validate_unhealthy_partitions (global_endpoint_manager , 0 )
353+ validate_stats (global_endpoint_manager , 2 , 2 , 2 , 2 , 0 , 0 )
354+ sleep (25 )
355+ perform_read_operation (READ ,
356+ fault_injection_container ,
357+ doc ['id' ],
358+ PK_VALUE ,
359+ expected_uri )
360+
361+ validate_stats (global_endpoint_manager , 2 , 3 , 1 , 0 , 0 , 0 )
362+ finally :
363+ _partition_health_tracker .REFRESH_INTERVAL_MS = 60 * 1000
364+
316365 @pytest .mark .parametrize ("read_operation, write_operation" , operations ())
317366 def test_service_request_error (self , read_operation , write_operation ):
318367 # the region should be tried 4 times before failing over and mark the partition as unavailable
@@ -333,8 +382,8 @@ def test_service_request_error(self, read_operation, write_operation):
333382
334383 # recover partition
335384 # remove faults and reduce initial recover time and perform a write
336- original_unavailable_time = _partition_health_tracker .INITIAL_UNAVAILABLE_TIME
337- _partition_health_tracker .INITIAL_UNAVAILABLE_TIME = 1
385+ original_unavailable_time = _partition_health_tracker .INITIAL_UNAVAILABLE_TIME_MS
386+ _partition_health_tracker .INITIAL_UNAVAILABLE_TIME_MS = 1
338387 custom_transport .faults = []
339388 try :
340389 perform_read_operation (read_operation ,
@@ -343,7 +392,7 @@ def test_service_request_error(self, read_operation, write_operation):
343392 PK_VALUE ,
344393 expected_uri )
345394 finally :
346- _partition_health_tracker .INITIAL_UNAVAILABLE_TIME = original_unavailable_time
395+ _partition_health_tracker .INITIAL_UNAVAILABLE_TIME_MS = original_unavailable_time
347396 validate_unhealthy_partitions (global_endpoint_manager , 0 )
348397
349398 custom_transport .add_fault (predicate ,
0 commit comments