ITADN

[Feature] Hedging Detection API — public accessors on CosmosDiagnostics

#5867OpenNaluTripician 创建于 2026-05-14
feature-requestneeds-investigation
N
NaluTripiciancommented
## Summary Add a public **Hedging Detection API** to `Microsoft.Azure.Cosmos.CosmosDiagnostics` so customers can post-hoc determine whether a successful or failed point/feed operation went through cross-region hedging, which regions were dispatched against, and which regions responded. The new surface is strictly additive, non-breaking, and is reachable from both `ItemResponse<T>.Diagnostics` (success path) and `CosmosException.Diagnostics` (error path). This is part of a cross-SDK feature being implemented in parallel across .NET, Java, and Python (with a spec-only deliverable for Rust). See cross-SDK design doc *"Hedging Detection API - Technical Design - Final"* (2026-05) for the contract. ## Public API additions ```csharp namespace Microsoft.Azure.Cosmos { public readonly struct RequestedRegion : IEquatable<RequestedRegion> { public string RegionName { get; } public RequestedRegionReason Reason { get; } public RequestedRegion(string regionName, RequestedRegionReason reason); public override bool Equals(object obj); public bool Equals(RequestedRegion other); public override int GetHashCode(); public override string ToString(); // "{regionName}:{reason}" } public enum RequestedRegionReason : byte { Initial, OperationRetry, TransportRetry, // reserved — not populated today (SE-001: signal lives in closed-source Direct package) Hedging, RegionFailover, CircuitBreakerProbe, } public abstract class CosmosDiagnostics { // Existing members unchanged. public virtual bool HedgingStarted() => false; public virtual IReadOnlyList<RequestedRegion> GetRequestedRegions() => Array.Empty<RequestedRegion>(); public virtual IReadOnlyList<string> GetRespondedRegions() => Array.Empty<string>(); } } ``` `CosmosTraceDiagnostics` (internal sealed) overrides all three to return live state. New state is populated at orchestrator dispatch sites (hedging strategy, retry policy, region failover, PPCB probe) and response-handling sites; appends are thread-safe via a private `object regionLock`. JSON shape of `Diagnostics.ToString()` is **unchanged** — new state is NOT serialized into the trace tree. ## Acceptance criteria (testable) - [ ] **AC1** Single-region client, ReadItem success, no retries → `HedgingStarted() == false`; `GetRequestedRegions().Count == 1` with reason `Initial`; `GetRespondedRegions().Count == 1`. - [ ] **AC2** Multi-region client, hedging enabled, primary responds under threshold → `HedgingStarted() == false`; `GetRequestedRegions().Count == 1` (**no phantom `Hedging` entry**); `GetRespondedRegions().Count == 1`. - [ ] **AC3** Multi-region client, hedging enabled, primary slow, hedge arm wins → `HedgingStarted() == true`; `GetRequestedRegions()` has ≥2 entries: `(primary, Initial)` and `(hedgeRegion, Hedging)`; `GetRespondedRegions()` has ≥1 entry. - [ ] **AC4** 410 Gone retry on same region → `GetRequestedRegions()` includes consecutive entries `(region, Initial)` then `(region, OperationRetry)`. - [ ] **AC5** Region failover after 503 → `GetRequestedRegions()` includes `(originalRegion, Initial)` then `(secondaryRegion, RegionFailover)`. - [ ] **AC6** PPCB probe after a region was previously circuit-broken → `GetRequestedRegions()` includes an entry with reason `CircuitBreakerProbe`. (Requires new `RequestContext` PPCB flag.) - [ ] **AC7** All-regions-down error → `CosmosException.Diagnostics.GetRequestedRegions()` non-empty; `GetRespondedRegions()` may be empty. - [ ] **AC8** `CosmosException.Diagnostics` exposes same data as the would-be-successful-response `Diagnostics`. - [ ] **AC9** Backward compat — existing customer subclass of `CosmosDiagnostics` that does NOT override the new methods returns the safe defaults and does not throw. - [ ] **AC10** Contract baselines (`DotNetSDKAPI.net6.json`, `DotNetSDKAPI.net8.json`) regenerated via `Microsoft.Azure.Cosmos\tools\UpdateContracts.ps1`; diffs committed; only the new public members appear. - [ ] **AC11** `CosmosDiagnostics.ToString()` JSON for a known fixed diagnostic input matches the baseline byte-for-byte (golden-file test). - [ ] **AC12** `RegionContactedInDiagnosticsBenchmark` mean latency does not regress by >5% vs baseline. - [ ] **AC13** Hedge arm registered then cancelled **before threshold delay elapses** → `GetRequestedRegions()` does NOT contain a `Hedging` entry for the cancelled region (uses fault injection; primary completes in <50% of threshold so `Task.Delay(threshold, ct)` throws `OperationCanceledException`). - [ ] **AC14** `GetRespondedRegions()` allows duplicates (same region producing two responses); XML doc warns that `Count > 1` does NOT imply multi-region success. - [ ] **AC15** Reason coverage — `TransportRetry` value never appears in v1 production paths (covered by a fault-injection AC test that enumerates the union of reasons across the full matrix; documents SE-001). - [ ] **AC16** Live multi-region smoke test (≥1) — runs against the team's existing multi-region test account with hedging enabled, injects primary-slow latency, and asserts `HedgingStarted() == true`, `GetRequestedRegions()` includes both regions, `GetRespondedRegions()` includes the secondary region. ## Files in scope - New: `Microsoft.Azure.Cosmos\src\Diagnostics\RequestedRegion.cs`, `RequestedRegionReason.cs` - Modify: `Microsoft.Azure.Cosmos\src\Diagnostics\CosmosDiagnostics.cs`, `CosmosTraceDiagnostics.cs`, `AvailabilityStrategy.cs` / `ParallelHedgingAvailabilityStrategy.cs`, `ClientRetryPolicy.cs`, `GlobalEndpointManager.cs`, `PartitionKeyRangeCache` response pipeline, `RequestContext` (PPCB flag), `changelog.md` - Tests: new `RequestedRegionTests.cs`, new `HedgingDetectionEmulatorTests.cs` (under `Microsoft.Azure.Cosmos.EmulatorTests\FaultInjection\`), live-account multi-region test - Contracts: `DotNetSDKAPI.net6.json`, `DotNetSDKAPI.net8.json` (regenerated) - OpenSpec: refresh `openspec/changes/hedging-detection-api/` (predecessor PR #5741 left artifacts on closed branch `feature/hedging-detection-api`) ## Out of scope - Wiring into OpenTelemetry / OTLP exporter — separate work item. - Changing the existing JSON `ToString()` shape — explicitly preserved. - Live wire-format changes — none; this is local diagnostics only. - Cross-SDK parity automation — handled at the cross-SDK design layer. ## Cross-SDK companion issues - **Java**: forthcoming companion issue in `Azure/azure-sdk-for-java` (`sdk/cosmos`). - **Python**: forthcoming companion issue in `Azure/azure-sdk-for-python` (`sdk/cosmos`). - **Rust**: forthcoming spec-only issue + draft PR in `Azure/azure-sdk-for-rust` (sequenced after PR #4330). ## Notes for the implementer - Full internal spec, landscape research, plan, risk register (`side-effects.json`), questions+answers, and an `.docx` review bundle are available from the workflow author (`@NaluTripician`) on request — they are team-only and not linked here. - Phase 1 review gate completed on 2026-05-14 with three substantive decisions: (Q3=B) Python collapsed to single Option B path; (Q5=B) Rust accepts `Retry → OperationRetry` JSON wire-format break with `#[serde(alias = "retry")]`; (Q10) at least one live multi-region smoke test required per implementing SDK. - This issue is being dispatched to the **Coding Agent Harness** for end-to-end implementation; reviewers may receive a draft PR shortly.
0 条评论