Skip to content

Commit 7d3e0df

Browse files
authored
[Cosmos] Address API view comments for release (#45086)
* version, date, changelog merge * rename all instances of `availability_strategy_config`, move client args to kwargs * Update CHANGELOG.md * refactor miss * more * Update cosmos_client.py * Update setup.py * Update CHANGELOG.md
1 parent a1a571d commit 7d3e0df

22 files changed

Lines changed: 293 additions & 282 deletions

sdk/cosmos/azure-cosmos/CHANGELOG.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,32 @@
11
## Release History
22

3-
### 4.15.0b3 (Unreleased)
3+
### 4.15.0 (2026-02-11)
44

55
#### Features Added
6-
7-
#### Breaking Changes
6+
* GA support of Per Partition Automatic Failover and AvailabilityStrategy features.
87

98
#### Bugs Fixed
109
* Fixed bug where sdk was encountering a timeout issue caused by infinite recursion during the 410 (Gone) error. See [PR 44770](https://github.com/Azure/azure-sdk-for-python/pull/44770)
1110
* Fixed crash in sync and async clients when `force_refresh_on_startup` was set to `None`, which could surface as `AttributeError: 'NoneType' object has no attribute '_WritableLocations'` during region discovery when `database_account` was `None`. See [PR 44987](https://github.com/Azure/azure-sdk-for-python/pull/44987)
1211

1312
#### Other Changes
1413
* Added tests for multi-language support for full text search. See [PR 44254](https://github.com/Azure/azure-sdk-for-python/pull/44254)
14+
* Renamed `availability_strategy_config` introduced in 4.15.0b1 to `availability_strategy` for both sync and async clients. See [PR 45086](https://github.com/Azure/azure-sdk-for-python/pull/45086).
15+
16+
### 4.14.6 (2026-02-02)
17+
18+
#### Bugs Fixed
19+
* Fixed async client crash (`AttributeError: 'NoneType' object has no attribute '_WritableLocations'`) during region discovery when `database_account` was `None`. See [PR 44939](https://github.com/Azure/azure-sdk-for-python/pull/44939)
20+
21+
### 4.14.5 (2026-01-15)
1522

23+
#### Bugs Fixed
24+
* Fixed bug where sdk was encountering a timeout issue caused by infinite recursion during the 410 (Gone) error.See [PR 44659](https://github.com/Azure/azure-sdk-for-python/pull/44649)
25+
26+
### 4.14.4 (2026-01-12)
27+
28+
#### Bugs Fixed
29+
* Fixed bug where sdk was not properly retrying requests in some edge cases after partition splits.See [PR 44425](https://github.com/Azure/azure-sdk-for-python/pull/44425)
1630

1731
### 4.15.0b2 (2025-12-16)
1832

sdk/cosmos/azure-cosmos/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -952,7 +952,7 @@ Cross region hedging availability strategy improves availability and reduces lat
952952

953953
#### Enabling Cross Region Hedging
954954

955-
You can enable cross region hedging by passing the `availability_strategy_config` parameter as a dictionary to the `CosmosClient` or per-request. The most common configuration keys are `threshold_ms` (delay before sending a hedged request) and `threshold_steps_ms` (step interval for additional hedged requests).
955+
You can enable cross region hedging by passing the `availability_strategy` parameter as a dictionary to the `CosmosClient` or per-request. The most common configuration keys are `threshold_ms` (delay before sending a hedged request) and `threshold_steps_ms` (step interval for additional hedged requests).
956956

957957
#### Client-level configuration
958958

@@ -962,7 +962,7 @@ from azure.cosmos import CosmosClient
962962
client = CosmosClient(
963963
"<account-uri>",
964964
"<account-key>",
965-
availability_strategy_config={"threshold_ms": 150, "threshold_steps_ms": 50}
965+
availability_strategy={"threshold_ms": 150, "threshold_steps_ms": 50}
966966
)
967967
```
968968

@@ -973,7 +973,7 @@ client = CosmosClient(
973973
container.read_item(
974974
item="item_id",
975975
partition_key="pk_value",
976-
availability_strategy_config={"threshold_ms": 150, "threshold_steps_ms": 50}
976+
availability_strategy={"threshold_ms": 150, "threshold_steps_ms": 50}
977977
)
978978
```
979979

@@ -984,7 +984,7 @@ container.read_item(
984984
container.read_item(
985985
item="item_id",
986986
partition_key="pk_value",
987-
availability_strategy_config=None
987+
availability_strategy=None
988988
)
989989
```
990990

@@ -999,7 +999,7 @@ executor = ThreadPoolExecutor(max_workers=2)
999999
client = CosmosClient(
10001000
"<account-uri>",
10011001
"<account-key>",
1002-
availability_strategy_config={"threshold_ms": 150, "threshold_steps_ms": 50},
1002+
availability_strategy={"threshold_ms": 150, "threshold_steps_ms": 50},
10031003
availability_strategy_executor=executor
10041004
)
10051005
```
@@ -1013,7 +1013,7 @@ from azure.cosmos import CosmosClient
10131013
client = CosmosClient(
10141014
"<account-uri>",
10151015
"<account-key>",
1016-
availability_strategy_config={"threshold_ms": 150, "threshold_steps_ms": 50},
1016+
availability_strategy={"threshold_ms": 150, "threshold_steps_ms": 50},
10171017
availability_strategy_max_concurrency=2
10181018
)
10191019
```

sdk/cosmos/azure-cosmos/azure/cosmos/_availability_strategy_config.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from typing import Optional, Any
2525

2626

27-
class CrossRegionHedgingStrategyConfig:
27+
class CrossRegionHedgingStrategy:
2828
"""Configuration for cross-region request hedging strategy.
2929
3030
:param config: Dictionary containing configuration values, defaults to None
@@ -48,15 +48,15 @@ def __init__(self, config: Optional[dict[str, Any]] = None) -> None:
4848
if self.threshold_steps_ms <= 0:
4949
raise ValueError("threshold_steps_ms must be positive")
5050

51-
def _validate_hedging_config(config: Optional[dict[str, Any]]) -> Optional[CrossRegionHedgingStrategyConfig]:
52-
"""Validate and create a CrossRegionHedgingStrategyConfig.
51+
def _validate_hedging_strategy(config: Optional[dict[str, Any]]) -> Optional[CrossRegionHedgingStrategy]:
52+
"""Validate and create a CrossRegionHedgingStrategy.
5353
5454
:param config: Dictionary containing configuration values
5555
:type config: Optional[Dict[str, Any]]
5656
:returns: Validated configuration object
57-
:rtype: Optional[CrossRegionHedgingStrategyConfig]
57+
:rtype: Optional[CrossRegionHedgingStrategy]
5858
"""
5959
if config is None:
6060
return None
6161

62-
return CrossRegionHedgingStrategyConfig(config)
62+
return CrossRegionHedgingStrategy(config)

sdk/cosmos/azure-cosmos/azure/cosmos/_availability_strategy_handler.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,22 +72,22 @@ def execute_single_request_with_delay(
7272
:rtype: ResponseType
7373
"""
7474

75-
availability_strategy_config = request_params.availability_strategy_config
76-
if availability_strategy_config is None:
77-
raise ValueError("availability_strategy_config should not be null")
75+
availability_strategy = request_params.availability_strategy
76+
if availability_strategy is None:
77+
raise ValueError("availability_strategy should not be null")
7878

7979
delay: int
8080
if location_index == 0:
8181
# No delay for initial request
8282
delay = 0
8383
elif location_index == 1:
8484
# First hedged request after threshold
85-
delay = availability_strategy_config.threshold_ms
85+
delay = availability_strategy.threshold_ms
8686
else:
8787
# Subsequent requests after threshold steps
8888
steps = location_index - 1
89-
delay = (availability_strategy_config.threshold_ms +
90-
(steps * availability_strategy_config.threshold_steps_ms))
89+
delay = (availability_strategy.threshold_ms +
90+
(steps * availability_strategy.threshold_steps_ms))
9191

9292
if delay > 0:
9393
time.sleep(delay / 1000)

sdk/cosmos/azure-cosmos/azure/cosmos/_base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
'max_item_count': 'maxItemCount',
7676
'throughput_bucket': 'throughputBucket',
7777
'excluded_locations': Constants.Kwargs.EXCLUDED_LOCATIONS,
78-
"availability_strategy_config": Constants.Kwargs.AVAILABILITY_STRATEGY_CONFIG
78+
"availability_strategy": Constants.Kwargs.AVAILABILITY_STRATEGY
7979
}
8080

8181
# Cosmos resource ID validation regex breakdown:

sdk/cosmos/azure-cosmos/azure/cosmos/_constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class Kwargs:
109109
RETRY_WRITE: Literal["retry_write"] = "retry_write"
110110
"""Whether to retry write operations if they fail. Used either at client level or request level."""
111111
EXCLUDED_LOCATIONS: Literal["excludedLocations"] = "excludedLocations"
112-
AVAILABILITY_STRATEGY_CONFIG: Literal["availabilityStrategyConfig"] = "availabilityStrategyConfig"
112+
AVAILABILITY_STRATEGY: Literal["availabilityStrategy"] = "availabilityStrategy"
113113
"""Availability strategy config. Used either at client level or request level"""
114114

115115
class UserAgentFeatureFlags(IntEnum):

sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
from . import documents
5959
from . import http_constants, exceptions
6060
from ._auth_policy import CosmosBearerTokenCredentialPolicy
61-
from ._availability_strategy_config import _validate_hedging_config, CrossRegionHedgingStrategyConfig
61+
from ._availability_strategy_config import _validate_hedging_strategy, CrossRegionHedgingStrategy
6262
from ._base import _build_properties_cache
6363
from ._change_feed.change_feed_iterable import ChangeFeedIterable
6464
from ._change_feed.change_feed_state import ChangeFeedState
@@ -119,7 +119,7 @@ def __init__( # pylint: disable=too-many-statements
119119
auth: CredentialDict,
120120
connection_policy: Optional[ConnectionPolicy] = None,
121121
consistency_level: Optional[str] = None,
122-
availability_strategy_config: Optional[dict[str, Any]] = None,
122+
availability_strategy: Optional[dict[str, Any]] = None,
123123
availability_strategy_executor: Optional[ThreadPoolExecutor] = None,
124124
**kwargs: Any
125125
) -> None:
@@ -135,7 +135,7 @@ def __init__( # pylint: disable=too-many-statements
135135
The connection policy for the client.
136136
:param documents.ConsistencyLevel consistency_level:
137137
The default consistency policy for client operations.
138-
:param documents.'CrossRegionHedgingStrategyConfig' availability_strategy_config:
138+
:param documents.'CrossRegionHedgingStrategyConfig' availability_strategy:
139139
The availability strategy configuration for routing requests across regions.
140140
:param concurrent.futures.ThreadPoolExecutor availability_strategy_executor:
141141
The thread pool executor for handling availability strategy requests.
@@ -145,8 +145,8 @@ def __init__( # pylint: disable=too-many-statements
145145
"""
146146
self.client_id = str(uuid.uuid4())
147147
self.url_connection = url_connection
148-
self.availability_strategy_config: Optional[CrossRegionHedgingStrategyConfig] =\
149-
_validate_hedging_config(availability_strategy_config)
148+
self.availability_strategy: Optional[CrossRegionHedgingStrategy] =\
149+
_validate_hedging_strategy(availability_strategy)
150150
self.availability_strategy_executor: Optional[ThreadPoolExecutor] = availability_strategy_executor
151151
self.master_key: Optional[str] = None
152152
self.resource_tokens: Optional[Mapping[str, Any]] = None
@@ -2156,7 +2156,7 @@ def PatchItem(
21562156
headers,
21572157
options.get("partitionKey", None))
21582158
request_params.set_excluded_location_from_options(options)
2159-
request_params.set_availability_strategy_config(options, self.availability_strategy_config)
2159+
request_params.set_availability_strategy(options, self.availability_strategy)
21602160
request_params.availability_strategy_executor = self.availability_strategy_executor
21612161
base.set_session_token_header(self, headers, path, request_params, options)
21622162
request_params.set_retry_write(options, self.connection_policy.RetryNonIdempotentWrites)
@@ -2254,7 +2254,7 @@ def _Batch(
22542254
options.get("partitionKey", None))
22552255
request_params.set_excluded_location_from_options(options)
22562256
request_params.set_retry_write(options, self.connection_policy.RetryNonIdempotentWrites)
2257-
request_params.set_availability_strategy_config(options, self.availability_strategy_config)
2257+
request_params.set_availability_strategy(options, self.availability_strategy)
22582258
request_params.availability_strategy_executor = self.availability_strategy_executor
22592259

22602260
base.set_session_token_header(self, headers, path, request_params, options)
@@ -2319,7 +2319,7 @@ def DeleteAllItemsByPartitionKey(
23192319
headers,
23202320
options.get("partitionKey", None))
23212321
request_params.set_excluded_location_from_options(options)
2322-
request_params.set_availability_strategy_config(options, self.availability_strategy_config)
2322+
request_params.set_availability_strategy(options, self.availability_strategy)
23232323
request_params.availability_strategy_executor = self.availability_strategy_executor
23242324
_, last_response_headers = self.__Post(
23252325
path=path,
@@ -2782,7 +2782,7 @@ def Create(
27822782
headers,
27832783
options.get("partitionKey", None))
27842784
request_params.set_excluded_location_from_options(options)
2785-
request_params.set_availability_strategy_config(options, self.availability_strategy_config)
2785+
request_params.set_availability_strategy(options, self.availability_strategy)
27862786
request_params.availability_strategy_executor = self.availability_strategy_executor
27872787
base.set_session_token_header(self, headers, path, request_params, options)
27882788
request_params.set_retry_write(options, self.connection_policy.RetryNonIdempotentWrites)
@@ -2835,7 +2835,7 @@ def Upsert(
28352835
headers,
28362836
options.get("partitionKey", None))
28372837
request_params.set_excluded_location_from_options(options)
2838-
request_params.set_availability_strategy_config(options, self.availability_strategy_config)
2838+
request_params.set_availability_strategy(options, self.availability_strategy)
28392839
request_params.availability_strategy_executor = self.availability_strategy_executor
28402840
base.set_session_token_header(self, headers, path, request_params, options)
28412841
request_params.set_retry_write(options, self.connection_policy.RetryNonIdempotentWrites)
@@ -2886,7 +2886,7 @@ def Replace(
28862886
headers,
28872887
options.get("partitionKey", None))
28882888
request_params.set_excluded_location_from_options(options)
2889-
request_params.set_availability_strategy_config(options, self.availability_strategy_config)
2889+
request_params.set_availability_strategy(options, self.availability_strategy)
28902890
request_params.availability_strategy_executor = self.availability_strategy_executor
28912891
base.set_session_token_header(self, headers, path, request_params, options)
28922892
request_params.set_retry_write(options, self.connection_policy.RetryNonIdempotentWrites)
@@ -2936,7 +2936,7 @@ def Read(
29362936
headers,
29372937
options.get("partitionKey", None))
29382938
request_params.set_excluded_location_from_options(options)
2939-
request_params.set_availability_strategy_config(options, self.availability_strategy_config)
2939+
request_params.set_availability_strategy(options, self.availability_strategy)
29402940
request_params.availability_strategy_executor = self.availability_strategy_executor
29412941
base.set_session_token_header(self, headers, path, request_params, options)
29422942
result, last_response_headers = self.__Get(path, request_params, headers, **kwargs)
@@ -2985,7 +2985,7 @@ def DeleteResource(
29852985
base.set_session_token_header(self, headers, path, request_params, options)
29862986
request_params.set_retry_write(options, self.connection_policy.RetryNonIdempotentWrites)
29872987
request_params.set_excluded_location_from_options(options)
2988-
request_params.set_availability_strategy_config(options, self.availability_strategy_config)
2988+
request_params.set_availability_strategy(options, self.availability_strategy)
29892989
request_params.availability_strategy_executor = self.availability_strategy_executor
29902990
result, last_response_headers = self.__Delete(path, request_params, headers, **kwargs)
29912991
self.last_response_headers = last_response_headers
@@ -3250,7 +3250,7 @@ def __GetBodiesFromQueryResult(result: dict[str, Any]) -> list[dict[str, Any]]:
32503250
options.get("partitionKey", None)
32513251
)
32523252
request_params.set_excluded_location_from_options(options)
3253-
request_params.set_availability_strategy_config(options, self.availability_strategy_config)
3253+
request_params.set_availability_strategy(options, self.availability_strategy)
32543254
request_params.availability_strategy_executor = self.availability_strategy_executor
32553255

32563256
base.set_session_token_header(self, headers, path, request_params, options, partition_key_range_id)
@@ -3296,7 +3296,7 @@ def __GetBodiesFromQueryResult(result: dict[str, Any]) -> list[dict[str, Any]]:
32963296
req_headers,
32973297
options.get("partitionKey", None))
32983298
request_params.set_excluded_location_from_options(options)
3299-
request_params.set_availability_strategy_config(options, self.availability_strategy_config)
3299+
request_params.set_availability_strategy(options, self.availability_strategy)
33003300
request_params.availability_strategy_executor = self.availability_strategy_executor
33013301

33023302
if not is_query_plan:

sdk/cosmos/azure-cosmos/azure/cosmos/_request_object.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from concurrent.futures.thread import ThreadPoolExecutor
2727
from typing import Optional, Mapping, Any, Union
2828

29-
from ._availability_strategy_config import CrossRegionHedgingStrategyConfig
29+
from ._availability_strategy_config import CrossRegionHedgingStrategy
3030
from ._constants import _Constants as Constants
3131
from .documents import _OperationType
3232
from .http_constants import ResourceType
@@ -46,7 +46,7 @@ def __init__(
4646
self.endpoint_override = endpoint_override
4747
self.should_clear_session_token_on_session_read_failure: bool = False # pylint: disable=name-too-long
4848
self.headers = headers
49-
self.availability_strategy_config: Optional[CrossRegionHedgingStrategyConfig] = None
49+
self.availability_strategy: Optional[CrossRegionHedgingStrategy] = None
5050
self.availability_strategy_executor: Optional[ThreadPoolExecutor] = None
5151
self.availability_strategy_max_concurrency: Optional[int] = None
5252
self.use_preferred_locations: Optional[bool] = None
@@ -111,10 +111,10 @@ def set_retry_write(self, request_options: Mapping[str, Any], client_retry_write
111111
def set_excluded_locations_from_circuit_breaker(self, excluded_locations: list[str]) -> None: # pylint: disable=name-too-long
112112
self.excluded_locations_circuit_breaker = excluded_locations
113113

114-
def set_availability_strategy_config(
114+
def set_availability_strategy(
115115
self,
116116
options: Mapping[str, Any],
117-
client_strategy_config: Optional[CrossRegionHedgingStrategyConfig] = None) -> None:
117+
client_strategy_config: Optional[CrossRegionHedgingStrategy] = None) -> None:
118118
"""Sets the availability strategy config for this request from options.
119119
If not in options, uses the client's default strategy.
120120
@@ -126,11 +126,11 @@ def set_availability_strategy_config(
126126
"""
127127
# setup availabilityStrategy
128128
# First try to get from options
129-
if Constants.Kwargs.AVAILABILITY_STRATEGY_CONFIG in options:
130-
self.availability_strategy_config = options[Constants.Kwargs.AVAILABILITY_STRATEGY_CONFIG]
129+
if Constants.Kwargs.AVAILABILITY_STRATEGY in options:
130+
self.availability_strategy = options[Constants.Kwargs.AVAILABILITY_STRATEGY]
131131
# If not in options, use client default
132132
elif client_strategy_config is not None:
133-
self.availability_strategy_config = client_strategy_config
133+
self.availability_strategy = client_strategy_config
134134

135135
def should_cancel_request(self) -> bool:
136136
"""Check if this request should be cancelled due to parallel request completion.

0 commit comments

Comments
 (0)