Skip to content

Commit d681614

Browse files
committed
Add cold-start metadata cache cross-region hedging (port of dotnet #5923)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 30b7dfe commit d681614

12 files changed

Lines changed: 1187 additions & 4 deletions

sdk/cosmos/azure-cosmos/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### 4.16.2 (Unreleased)
44

55
#### Features Added
6+
* Added cold-start metadata cache cross-region hedging. When enabled, the SDK hedges the first-time population of the container and partition-key-range metadata caches to a second region if the primary region is slow, returning the first acceptable response. Controlled by the new `enable_metadata_hedging_for_cold_start` client keyword (tri-state: `None` follows the account's PPAF state, `True` forces it on, `False` disables it).
67

78
#### Breaking Changes
89

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

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,22 @@
2727
DEFAULT_THRESHOLD_MS = 500
2828
DEFAULT_THRESHOLD_STEPS_MS = 100
2929

30+
# Defaults for cold-start metadata cache hedging. These are SDK-derived and not
31+
# customer-configurable. The threshold mirrors the .NET design (first-attempt
32+
# control-plane timeout ~1s + a 500ms step => 1500ms). The concurrency budget
33+
# caps the number of in-flight metadata hedges per client.
34+
DEFAULT_METADATA_HEDGING_THRESHOLD_MS = 1500
35+
DEFAULT_METADATA_HEDGING_THRESHOLD_STEPS_MS = 500
36+
DEFAULT_METADATA_HEDGING_CONCURRENCY_BUDGET = 8
37+
3038

3139
class CrossRegionHedgingStrategy:
3240
"""Configuration for cross-region request hedging strategy.
3341
3442
:param config: Dictionary containing configuration values, defaults to None
3543
:type config: Optional[Dict[str, Any]]
3644
:raises ValueError: If configuration values are invalid
37-
45+
3846
The config dictionary can contain:
3947
- threshold_ms: Time in ms before routing to alternate region (default: 500)
4048
- threshold_steps_ms: Time interval between routing attempts (default: 100)
@@ -53,11 +61,49 @@ def __init__(self, config: Optional[dict[str, Any]] = None) -> None:
5361
raise ValueError("threshold_steps_ms must be positive")
5462

5563

64+
class MetadataCrossRegionHedgingStrategy(CrossRegionHedgingStrategy):
65+
"""Cold-start metadata cache cross-region hedging configuration.
66+
67+
Unlike :class:`CrossRegionHedgingStrategy`, the metadata hedging threshold is a
68+
fixed, SDK-derived value and is not customer-configurable. The strategy is bounded
69+
to the primary request plus a single cross-region hedge.
70+
"""
71+
72+
def __init__(self) -> None:
73+
super().__init__(
74+
{
75+
"threshold_ms": DEFAULT_METADATA_HEDGING_THRESHOLD_MS,
76+
"threshold_steps_ms": DEFAULT_METADATA_HEDGING_THRESHOLD_STEPS_MS,
77+
}
78+
)
79+
80+
81+
def resolve_metadata_hedging_opt_in(opt_in: Optional[bool], ppaf_enabled: bool) -> bool:
82+
"""Resolve the tri-state cold-start metadata hedging opt-in to a concrete bool.
83+
84+
When the customer leaves the opt-in ``None`` (the default), cold-start metadata
85+
hedging follows the account's PPAF (Per-Partition Automatic Failover) state: it is
86+
enabled when PPAF is enabled and disabled otherwise. An explicit ``True`` enables
87+
hedging even when PPAF is disabled, and an explicit ``False`` disables it regardless
88+
of PPAF.
89+
90+
:param opt_in: The customer-supplied tri-state opt-in (True/False/None).
91+
:type opt_in: Optional[bool]
92+
:param ppaf_enabled: Whether Per-Partition Automatic Failover is enabled for the account.
93+
:type ppaf_enabled: bool
94+
:returns: True if cold-start metadata hedging should be applied, False otherwise.
95+
:rtype: bool
96+
"""
97+
if opt_in is None:
98+
return ppaf_enabled
99+
return opt_in
100+
101+
56102
def _validate_request_hedging_strategy(
57103
config: Optional[Union[bool, dict[str, Any]]]
58104
) -> Union[CrossRegionHedgingStrategy, bool, None]:
59105
"""Validate and create a CrossRegionHedgingStrategy for a request.
60-
106+
61107
:param config: Configuration for availability strategy. Can be:
62108
- None: Returns None (no strategy, uses client default if available)
63109
- True: Returns strategy with default values (threshold_ms=500, threshold_steps_ms=100)

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
from . import http_constants, exceptions
6363
from ._auth_policy import CosmosBearerTokenCredentialPolicy
6464
from ._availability_strategy_config import validate_client_hedging_strategy, CrossRegionHedgingStrategy
65+
from ._metadata_hedging import MetadataCrossRegionHedgingHandler
6566
from ._base import _build_properties_cache
6667
from ._change_feed.change_feed_iterable import ChangeFeedIterable
6768
from ._change_feed.change_feed_state import ChangeFeedState
@@ -143,6 +144,7 @@ def __init__( # pylint: disable=too-many-statements
143144
consistency_level: Optional[str] = None,
144145
availability_strategy: Union[bool, dict[str, Any]] = False,
145146
availability_strategy_executor: Optional[ThreadPoolExecutor] = None,
147+
enable_metadata_hedging_for_cold_start: Optional[bool] = None,
146148
**kwargs: Any
147149
) -> None:
148150
"""
@@ -170,6 +172,12 @@ def __init__( # pylint: disable=too-many-statements
170172
self.availability_strategy: Union[CrossRegionHedgingStrategy, None] =\
171173
validate_client_hedging_strategy(availability_strategy)
172174
self.availability_strategy_executor: Optional[ThreadPoolExecutor] = availability_strategy_executor
175+
# Tri-state opt-in for cold-start metadata cache cross-region hedging. None follows the
176+
# account's PPAF state, True forces it on, False disables it (no handler is created).
177+
self._metadata_hedging_opt_in: Optional[bool] = enable_metadata_hedging_for_cold_start
178+
self._metadata_hedging_handler: Optional[MetadataCrossRegionHedgingHandler] = (
179+
None if enable_metadata_hedging_for_cold_start is False else MetadataCrossRegionHedgingHandler()
180+
)
173181
self.master_key: Optional[str] = None
174182

175183
self.resource_tokens: Optional[Mapping[str, Any]] = None

0 commit comments

Comments
 (0)