Skip to content

Commit 2c28269

Browse files
Implement Hedging Detection API alongside its spec
Land the Hedging Detection API implementation described by HEDGING_DETECTION_API_SPEC.md in the same PR as the spec, since the change is small enough to combine. Driver (azure_data_cosmos_driver): - Add `OperationRetry` to `ExecutionContext`; deprecate `Retry` (serialized form changes "retry" -> "operation_retry"). Update `as_str()` and all dispatch sites in the operation/transport pipelines. - Add public `RequestedRegion` struct and `RequestedRegionReason` enum (both `#[non_exhaustive]`) plus a total `From<ExecutionContext>` mapping. - Add `DiagnosticsContext::requested_regions()` (dispatch order, duplicates, per-region reason), `responded_regions()` (completion order, service replies only via a new `responded_with_service_reply()` predicate), and `hedging_started()` (ExecutionContext::Hedging predicate; the hedge_diagnostics signal lights up once the hedging implementation lands). - Export the two new types from the diagnostics module. - Add unit tests for the accessors and mapping totality. SDK (azure_data_cosmos): - Re-export `RequestedRegion` / `RequestedRegionReason`, mirroring `CosmosDiagnosticsContext`. Docs/CHANGELOGs: - Reconcile the spec's "spec-only" framing now that the implementation lands together, and add CHANGELOG entries to both crates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 0d8f8c6 commit 2c28269

9 files changed

Lines changed: 403 additions & 77 deletions

File tree

sdk/cosmos/azure_data_cosmos/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
- Added configurable retry limits for throttled (HTTP 429, rate-limited) requests, mirroring the .NET and Java SDKs' `ThrottlingRetryOptions`. A new nested `ThrottlingRetryOptions` group on `OperationOptions` (field `throttling_retry_options`) carries `max_retry_count` (env `AZURE_COSMOS_MAX_THROTTLE_RETRY_COUNT`, default `9`, `0` disables throttle retries) and `max_retry_wait_time` (default `30s`), settable per-request via `OperationOptions`/`OperationOptionsBuilder` and `ThrottlingRetryOptionsBuilder`. New client-wide setter `CosmosClientBuilder::with_throttling_retry_options(ThrottlingRetryOptions)` forwards the group as runtime-layer defaults. Both budgets apply *per transport-pipeline invocation*, not per logical operation — an operation that fans out across regions (failover, hedging) starts a fresh budget per leg; use `OperationOptions::end_to_end_latency_policy` to bound total per-operation wall-clock time. ([#4544](https://github.com/Azure/azure-sdk-for-rust/pull/4544))
1010

11+
- Added the cross-SDK Hedging Detection API on `DiagnosticsContext`: `hedging_started() -> bool`, `requested_regions() -> Vec<RequestedRegion>` (dispatch order, duplicates allowed, each entry tagged with a `RequestedRegionReason`), and `responded_regions() -> Vec<&Region>` (arrival order, duplicates allowed). Re-exports the `RequestedRegion` struct and `RequestedRegionReason` enum (defined in `azure_data_cosmos_driver`) that realize the cross-SDK contract's per-region-reason surface. `RequestedRegionReason` is `#[non_exhaustive]`; `TransportRetry` and `CircuitBreakerProbe` are reserved and not populated by the initial implementation. ([#4558](https://github.com/Azure/azure-sdk-for-rust/pull/4558))
12+
1113
### Breaking Changes
1214

1315
### Bugs Fixed

sdk/cosmos/azure_data_cosmos/docs/HEDGING_DETECTION_API_SPEC.md

Lines changed: 59 additions & 62 deletions
Large diffs are not rendered by default.

sdk/cosmos/azure_data_cosmos/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ pub use error::{CosmosError, CosmosStatus, Result, SubStatusCode};
3636
pub(crate) use azure_data_cosmos_driver::error::CosmosError as DriverCosmosError;
3737
pub use models::{
3838
BatchResponse, CosmosNumber, DiagnosticsContext, ItemResponse, PatchInstructions,
39-
PatchOperation, ResourceResponse, ResponseBody, ResponseHeaders,
39+
PatchOperation, RequestedRegion, RequestedRegionReason, ResourceResponse, ResponseBody,
40+
ResponseHeaders,
4041
};
4142
pub use options::*;
4243
pub use query::Query;

sdk/cosmos/azure_data_cosmos/src/models/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ pub use azure_data_cosmos_driver::models::{
4141
#[doc(inline)]
4242
pub use azure_data_cosmos_driver::diagnostics::DiagnosticsContext;
4343

44+
// Re-export the cross-SDK Hedging Detection API's per-region-reason types,
45+
// which back `DiagnosticsContext::requested_regions()`. They are defined
46+
// in the driver (the accessors are inherent methods on `DiagnosticsContext`)
47+
// and surfaced here, mirroring the `DiagnosticsContext` re-export above.
48+
#[doc(inline)]
49+
pub use azure_data_cosmos_driver::diagnostics::{RequestedRegion, RequestedRegionReason};
50+
4451
fn deserialize_cosmos_timestamp<'de, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
4552
where
4653
D: Deserializer<'de>,

sdk/cosmos/azure_data_cosmos_driver/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
- Added `PartitionKeyDefinition::with_kind(PartitionKeyKind)` and `PartitionKeyDefinition::with_version(PartitionKeyVersion)` consuming setters so callers can override the auto-inferred kind and the default `V2` version without resorting to private-field workarounds. `PartitionKeyDefinition::new(paths)` continues to auto-infer the kind from the path count.
3636
- Reshaped `PatchInstructions` API so that `PatchInstructions::new()` creates an empty instruction set, where individual operations can be added via `with_operation()`. The `PatchInstructions` type also implements `From<Vec<PatchOperation>>` for ergonomic construction from a vector.
3737
- Refactored the driver response surface: introduced `ResponseBody` (a `NoPayload` / `Bytes(Bytes)` / `Items(Vec<Bytes>)` enum with `single()`, `items()`, `into_single::<T>()`, `into_items::<T>()`, and `is_empty()` helpers), added typed `CosmosRequestHeaders` fields for query / changefeed headers (`max_item_count`, `incremental_feed`, `populate_index_metrics`, `populate_query_metrics`, `enable_cross_partition_query`) so callers no longer need raw `custom_headers`, the pipeline now auto-emits `x-ms-documentdb-isquery: True` and `Content-Type: application/query+json` for `OperationType::Query`, and `CosmosStatus` gained `PartialEq<StatusCode>`, `From<CosmosStatus> for StatusCode/u16`, and a `CosmosStatus::new(StatusCode)` constructor. ([#4401](https://github.com/Azure/azure-sdk-for-rust/pull/4401))
38+
39+
- Added the cross-SDK Hedging Detection API accessors on `DiagnosticsContext`: `requested_regions() -> Vec<RequestedRegion>` (dispatch order, duplicates allowed, each entry tagged with a `RequestedRegionReason`), `responded_regions() -> Vec<&Region>` (arrival order by completion time, duplicates allowed, service replies only), and `hedging_started() -> bool` (`true` only once a hedge fan-out is dispatched). Added the public `RequestedRegion` struct and `RequestedRegionReason` enum (both `#[non_exhaustive]`, re-exported by `azure_data_cosmos`) plus a `From<ExecutionContext>` mapping. `TransportRetry` and `CircuitBreakerProbe` reasons are reserved and not populated by the initial implementation. ([#4558](https://github.com/Azure/azure-sdk-for-rust/pull/4558))
3840
- Added support for the `x-ms-cosmos-hub-region-processing-only` request header on retries after a `404 / 1002 (READ_SESSION_NOT_AVAILABLE)` response on single-master data-plane Cosmos operations. The header asks the backend to route only to a region that has caught up to the requested LSN, reducing the chance of a follow-up retry hitting a region whose session is also behind. The header is scoped to single-master accounts (multi-master accounts already have a different recovery path) and to data-plane operations (metadata-pipeline operations are out of scope per the design spec). Once latched on the first 1002 within an operation, the header is emitted on every subsequent retry for that operation. ([#4389](https://github.com/Azure/azure-sdk-for-rust/pull/4389))
3941
- Added local query-plan generator scaffolding under `crate::query` (lexer, parser, AST, planner, and in-memory evaluator). The scaffolding is **not wired into the production query path** yet — production callers still issue Gateway query-plan requests via `CosmosOperation::query_plan`. The `__internal_testing` cargo feature exposes `query::__test_only_generate_query_plan_for_pk_paths`, `query::__TEST_ONLY_SUPPORTED_QUERY_FEATURES`, and `CosmosOperation::query_plan` for cross-crate gateway-comparison tests; this feature is intentionally unstable and **not covered by SemVer**.
4042
- Added per-partition automatic failover (PPAF) for writes on single-master accounts. On 403/3 WriteForbidden, 503 ServiceUnavailable, 429/3092 SystemResourceUnavailable, 410/1022 Gone, or 408 RequestTimeout from a region, the affected partition is failed over to the next preferred region; subsequent writes for that partition skip the failed region. ([#4156](https://github.com/Azure/azure-sdk-for-rust/pull/4156))
@@ -48,6 +50,7 @@
4850
- Renamed the error surface: `Error``CosmosError`, `ErrorBuilder``CosmosErrorBuilder`. Categorization moved from a `Kind` enum to predicates on `CosmosStatus` (`is_not_found()`, `is_throttled()`, `is_transient()`, …); error details are reached via `status()` and `response()` instead of the previous flat accessors. ([#4442](https://github.com/Azure/azure-sdk-for-rust/pull/4442))
4951
- Renamed `MaxItemCount` to `MaxItemCountHint` to better reflect that the value is a hint to the service (which may return fewer items) rather than a strict cap. The SDK already exposed the type under the new name via a `use ... as MaxItemCountHint`; the rename makes the canonical name consistent across both crates. Update callers that reference `azure_data_cosmos_driver::models::MaxItemCount`, `CosmosRequestHeaders::max_item_count` typings, or `CosmosOperation::with_max_item_count` argument types accordingly.
5052
- Marked `PartitionKeyVersion` and `CosmosStatus` as `#[non_exhaustive]` to allow future variants/fields to be added without further breaking changes. Callers must use `..` wildcard arms when matching on `PartitionKeyVersion`; `CosmosStatus` already had private fields and is constructed via `CosmosStatus::new` / `with_sub_status`, so the attribute is primarily a forward-compat signal.
53+
- Renamed `ExecutionContext::Retry` to `ExecutionContext::OperationRetry` to align with the cross-SDK reason taxonomy (`OperationRetry` distinguishes operation-level retries from `TransportRetry`). The serialized diagnostics string changes from `"retry"` to `"operation_retry"`; the old variant is retained as `#[deprecated]` for one release. Telemetry parsers that match on the string `"retry"` must be updated. (No `#[serde(rename = "retry")]` is applied.) ([#4558](https://github.com/Azure/azure-sdk-for-rust/pull/4558))
5154
- Slimmed the cached `PartitionKeyRange` to six fields, dropping eight metadata fields the routing-map cache never reads (`resource_id`, `self_link`, `etag`, `timestamp`, `rid_prefix`, `target_throughput`, `lsn`, `owned_archival_pk_range_ids`). The struct now retains the four fields the routing layer consults (`id`, `min_inclusive`, `max_exclusive`, `status`) plus `throughput_fraction` and `parents`, kept on the cached representation for downstream consumers that read them directly. As part of this change, `PartialEq` and `Hash` no longer hash `resource_id`: two ranges with the same `id` / `min_inclusive` / `max_exclusive` are now equal regardless of their `_rid`. Internal callers never used `PartitionKeyRange` as a hash-map key, but downstream consumers that did so should review their assumptions. Service responses are unchanged on the wire — the dropped JSON fields are silently ignored by serde on deserialization. ([#4393](https://github.com/Azure/azure-sdk-for-rust/pull/4393))
5255
- Changed `CosmosResponse::diagnostics()` to return `Arc<DiagnosticsContext>` instead of `&DiagnosticsContext`. The returned `Arc` derefs transparently for read-only inspection (existing call patterns like `response.diagnostics().activity_id()` continue to work), but bindings of the form `let d = response.diagnostics();` now own a cloned `Arc` handle rather than a borrow — letting callers retain operation diagnostics across `into_body()`. Replaces the additive `CosmosResponse::diagnostics_arc()` accessor introduced earlier in this preview cycle.
5356
- Removed `CosmosResponse::body() -> &[u8]`. The previous accessor panicked on multi-item feed bodies, which is unsafe for a public API. The non-consuming `body_parts() -> &ResponseBody` accessor has been renamed to `body()`. Callers needing borrowed access should pattern-match on `ResponseBody::Bytes(b)` / `ResponseBody::Items(items)`; consuming callers can use `into_body().single_item::<T>()` or `into_body().single()`.

0 commit comments

Comments
 (0)