13
13
from azure .cosmos import CosmosClient
14
14
from azure .cosmos .exceptions import CosmosHttpResponseError
15
15
from _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 , \
17
18
QUERY_PK , QUERY , CHANGE_FEED , CHANGE_FEED_PK , CHANGE_FEED_EPK , READ_ALL_ITEMS , REGION_1 , REGION_2 , \
18
19
write_operations_and_errors , validate_unhealthy_partitions , read_operations_and_errors , PK_VALUE , operations , \
19
- create_doc
20
+ create_doc , validate_stats
20
21
from test_per_partition_circuit_breaker_mm_async import DELETE_ALL_ITEMS_BY_PARTITION_KEY
21
22
22
23
def perform_write_operation (operation , container , fault_injection_container , doc_id , pk , expected_uri ):
@@ -99,15 +100,18 @@ class TestPerPartitionCircuitBreakerMM:
99
100
host = test_config .TestConfig .host
100
101
master_key = test_config .TestConfig .masterKey
101
102
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
103
104
104
105
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
105
109
client = CosmosClient (default_endpoint , self .master_key ,
106
110
preferred_locations = [REGION_1 , REGION_2 ],
107
111
multiple_write_locations = True ,
108
112
transport = custom_transport , ** kwargs )
109
113
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 )
111
115
return {"client" : client , "db" : db , "col" : container }
112
116
113
117
@pytest .mark .parametrize ("write_operation, error" , write_operations_and_errors ())
@@ -151,8 +155,8 @@ def test_write_consecutive_failure_threshold(self, write_operation, error):
151
155
152
156
validate_unhealthy_partitions (global_endpoint_manager , 1 )
153
157
# 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
156
160
custom_transport .faults = []
157
161
try :
158
162
perform_write_operation (write_operation ,
@@ -162,7 +166,7 @@ def test_write_consecutive_failure_threshold(self, write_operation, error):
162
166
PK_VALUE ,
163
167
uri_down )
164
168
finally :
165
- _partition_health_tracker .INITIAL_UNAVAILABLE_TIME = original_unavailable_time
169
+ _partition_health_tracker .INITIAL_UNAVAILABLE_TIME_MS = original_unavailable_time
166
170
validate_unhealthy_partitions (global_endpoint_manager , 0 )
167
171
168
172
@pytest .mark .cosmosCircuitBreakerMultiRegion
@@ -203,8 +207,8 @@ def test_read_consecutive_failure_threshold(self, read_operation, error):
203
207
expected_unhealthy_partitions = 1
204
208
validate_unhealthy_partitions (global_endpoint_manager , expected_unhealthy_partitions )
205
209
# 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
208
212
custom_transport .faults = []
209
213
try :
210
214
perform_read_operation (read_operation ,
@@ -213,7 +217,7 @@ def test_read_consecutive_failure_threshold(self, read_operation, error):
213
217
doc ['pk' ],
214
218
uri_down )
215
219
finally :
216
- _partition_health_tracker .INITIAL_UNAVAILABLE_TIME = original_unavailable_time
220
+ _partition_health_tracker .INITIAL_UNAVAILABLE_TIME_MS = original_unavailable_time
217
221
validate_unhealthy_partitions (global_endpoint_manager , 0 )
218
222
219
223
@pytest .mark .parametrize ("write_operation, error" , write_operations_and_errors ())
@@ -297,7 +301,7 @@ def test_read_failure_rate_threshold(self, read_operation, error):
297
301
# restore minimum requests
298
302
_partition_health_tracker .MINIMUM_REQUESTS_FOR_FAILURE_RATE = 100
299
303
300
- def setup_info (self , error ):
304
+ def setup_info (self , error , ** kwargs ):
301
305
expected_uri = _location_cache .LocationCache .GetLocationalEndpoint (self .host , REGION_2 )
302
306
uri_down = _location_cache .LocationCache .GetLocationalEndpoint (self .host , REGION_1 )
303
307
custom_transport = FaultInjectionTransport ()
@@ -307,12 +311,57 @@ def setup_info(self, error):
307
311
FaultInjectionTransport .predicate_targets_region (r , uri_down ))
308
312
custom_transport .add_fault (predicate ,
309
313
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 )
311
315
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 )
313
317
container = setup ['col' ]
314
318
return container , doc , expected_uri , uri_down , fault_injection_container , custom_transport , predicate
315
319
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
+
316
365
@pytest .mark .parametrize ("read_operation, write_operation" , operations ())
317
366
def test_service_request_error (self , read_operation , write_operation ):
318
367
# 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):
333
382
334
383
# recover partition
335
384
# 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
338
387
custom_transport .faults = []
339
388
try :
340
389
perform_read_operation (read_operation ,
@@ -343,7 +392,7 @@ def test_service_request_error(self, read_operation, write_operation):
343
392
PK_VALUE ,
344
393
expected_uri )
345
394
finally :
346
- _partition_health_tracker .INITIAL_UNAVAILABLE_TIME = original_unavailable_time
395
+ _partition_health_tracker .INITIAL_UNAVAILABLE_TIME_MS = original_unavailable_time
347
396
validate_unhealthy_partitions (global_endpoint_manager , 0 )
348
397
349
398
custom_transport .add_fault (predicate ,
0 commit comments