ITADN

Tracking (Direct/msdata): Add Range.CheckOverlapping overload accepting custom IComparer<T> — blocker for #5897

#5899Openananth7592 创建于 2026-05-21
bugQUERYRoutingHierarchicalPartitioning
A
ananth7592commented
## Tracking issue — implementation work happens in the internal `msdata/CosmosDB` repository This is a public tracker for changes to the **Direct package** (`Microsoft.Azure.Cosmos.Direct`) that the v3 SDK depends on. The actual code change happens in the internal `msdata/CosmosDB` repository in `Product/Microsoft.Azure.Documents/SharedFiles/Routing/Range.cs`. Filing it here so the dependency, blocker status, and rollout sequence are visible from the public v3 SDK repo. **Type:** Direct-package bug; required dependency for #5897. --- ## Summary `Microsoft.Azure.Documents.Routing.Range<T>.CheckOverlapping(Range<T>, Range<T>)` (Direct package, `internal sealed`) is **hardcoded** to use `Comparer<T>.Default` for all boundary comparisons. For `T = string`, that is `StringComparer.Ordinal`. There is no overload that accepts a caller-supplied `IComparer<T>`. The v3 SDK's `CollectionRoutingMap.GetOverlappingRanges`: 1. Binary-searches a `SortedList<Range<string>, …>` with a **swappable** comparer — `LengthAwareMin/MaxComparer` (which normalize trailing `'0'` bytes via `TrimEnd('0')`) is the production default for HPK containers. 2. Verifies overlap with `Range<string>.CheckOverlapping` — which uses **ordinal** semantics. When stored boundaries differ from input EPKs only in trailing-zero padding (the scenario the backend Elasticity team mitigated for the affected customer on 2026-05-15), the two stages disagree, producing an empty overlapping-range list. Consumer impact: - Silent zero-result query (data loss to the application). - `ArgumentOutOfRangeException` from `RequestInvokerHandler.SendAsync` (`overlappingRanges[0]` on empty list) — see production telemetry in #5859. The principled fix for the v3 consumer (#5897) cannot land until the Direct package exposes an overload of `CheckOverlapping` that accepts the same `IComparer<T>` used for the surrounding binary search. --- ## Proposed change (Direct package, `Range.cs`) ```csharp namespace Microsoft.Azure.Documents.Routing { internal sealed partial class Range<T> { // NEW: overload accepting the caller's comparer. public static bool CheckOverlapping(Range<T> range1, Range<T> range2, IComparer<T> boundsComparer) { if (boundsComparer == null) throw new ArgumentNullException(nameof(boundsComparer)); if (range1 == null || range2 == null || range1.IsEmpty || range2.IsEmpty) return false; int cmp1 = boundsComparer.Compare(range1.Min, range2.Max); int cmp2 = boundsComparer.Compare(range2.Min, range1.Max); if (cmp1 <= 0 && cmp2 <= 0) { if ((cmp1 == 0 && !(range1.IsMinInclusive && range2.IsMaxInclusive)) || (cmp2 == 0 && !(range2.IsMinInclusive && range1.IsMaxInclusive))) { return false; } return true; } return false; } // PRESERVED: legacy overload, now a thin delegator. Bit-identical output to today. public static bool CheckOverlapping(Range<T> range1, Range<T> range2) => CheckOverlapping(range1, range2, TComparer); } } ``` **Optional but recommended** companion: make `Range<T>.MinComparer` / `Range<T>.MaxComparer` constructible with a user-supplied bounds comparer (mirroring the public ctor that already exists on `LengthAwareMin/MaxComparer`). Static `Instance` fields preserved for binary compatibility. No existing call site changes. --- ## Acceptance criteria - [ ] New public overload `Range<T>.CheckOverlapping(Range<T>, Range<T>, IComparer<T>)` exists in `Product/Microsoft.Azure.Documents/SharedFiles/Routing/Range.cs`. - [ ] Existing parameterless-comparer overload preserved; delegates to the new overload with `TComparer`; output is bit-identical for every existing call site. - [ ] (Recommended) `MinComparer` / `MaxComparer` expose a public ctor accepting `IComparer<T>`; `Instance` static field preserved. - [ ] Unit tests added in the Direct package: - `Range_CheckOverlapping_WithCustomComparer_AgreesWithLengthAware_OnZeroPaddedBoundaries` — covers the exact failure pattern (96-char EPK vs 128-char zero-padded boundary range using a `LengthAware`-style comparer). - `Range_CheckOverlapping_WithCustomComparer_PreservesOrdinalSemantics_WhenComparerIsOrdinal` — round-trip equivalence with the legacy overload. - `Range_CheckOverlapping_ThrowsOnNullComparer`. - Property/table-driven test verifying commutativity: `CheckOverlapping(a, b, c) == CheckOverlapping(b, a, c)`. - [ ] All existing tests for `Range<T>`, `CollectionRoutingMap`, and the Direct routing layer pass unchanged. - [ ] `internal sealed` preserved — no public API surface exposure. - [ ] Direct package release notes call out the additive overload. - [ ] New Direct package version published. - [ ] v3 SDK `Directory.Build.props` Direct floor bumped (driven by #5897). --- ## Rollout sequence 1. Merge the `Range.cs` change into `master` of `msdata/CosmosDB`. 2. Publish a new `Microsoft.Azure.Cosmos.Direct` package version containing the overload. 3. Bump the floor in `Azure/azure-cosmos-dotnet-v3 :: Microsoft.Azure.Cosmos/Directory.Build.props` (driven by #5897). 4. Wire the v3 SDK consumer (`CollectionRoutingMap.GetOverlappingRanges`) to pass the length-aware comparer through to the new overload (driven by #5897). 5. Verify end-to-end via the v3 unit tests added under #5897 — the test that today fails with empty results must return the correct overlapping-range list. --- ## Risk / blast radius | Surface | Impact | |---|---| | Existing callers of `CheckOverlapping(Range<T>, Range<T>)` | **None.** Delegates to new overload with `TComparer` → bit-identical output. | | Other components depending on `Range<T>` (`PartitionKeyRange`, `RoutingMapProvider`, `BackendRequestRouter`, gateway query planner) | **None.** Pure addition; no signature changes; `internal sealed` preserved. | | Public API of Direct package | **None.** Type stays `internal`. | | Performance | **Negligible.** One extra virtual dispatch on the new overload only; existing call sites unchanged. | | Binary compatibility | **Preserved.** Additive method; legacy overload retained. | --- ## References - #5859 — investigation umbrella & production telemetry (MFS `eastus2euap` canary, 4 AOOR occurrences in May 2026) - #5897 — v3-side principled fix that depends on this Direct overload - #5898 — v3-side defensive `Count == 0` guard in `RequestInvokerHandler.SendAsync` (lands independently) - `msdata/CosmosDB :: Product/Microsoft.Azure.Documents/SharedFiles/Routing/Range.cs` — file to modify - `Azure/azure-cosmos-dotnet-v3 :: Microsoft.Azure.Cosmos/src/Routing/CollectionRoutingMap.cs` — downstream v3 consumer (`GetOverlappingRanges`) --- *This issue is a public tracker only — implementation, PR, and review happen inside `msdata/CosmosDB`. Status updates will be posted here as comments to keep the v3-SDK dependency chain visible.*
0 条评论