cosmos: scope cross-partition queries to their requested feed range#4726
cosmos: scope cross-partition queries to their requested feed range#4726ananth7592 wants to merge 4 commits into
Conversation
A cross-partition query scoped to a sub-partition feed range via `ContainerClient::query_items` with `FeedScope::range([X, Y))` ignored the scope and scanned the whole container, returning documents outside the requested effective-partition-key window. Root cause: `plan_fresh` / `plan_resume_from_saved_snapshot` built request ranges solely from the backend query plan's `query_ranges` (which cover the whole container for an unfiltered `SELECT`) and never intersected them with `CosmosOperation::target()` (the scope feed range). Fix: - Clip each query-plan range to the operation's scope before resolving topology (a full-key-space scope imposes no restriction; a range entirely outside the scope is skipped). - Tag the emitted `x-ms-start-epk` / `x-ms-end-epk` window with `x-ms-read-key-type: EffectivePartitionKeyRange` (matching .NET/Java), and only when a bound is actually written. Previously the point value `EffectivePartitionKey` was sent alongside the range window, which the gateway rejected with a bare HTTP 400 once scoped windows started flowing. Tests: planner unit tests for scoped vs full-container fan-out; apply_headers unit tests for the read-key-type value and whole-partition omission; an emulator end-to-end regression test asserting interior windows return exactly the scoped subset. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes a Cosmos cross-partition query bug (#4725) where a query scoped to a sub-partition feed range (ContainerClient::query_items with FeedScope::range(..)) ignored the scope and scanned the whole container, returning documents outside the requested effective-partition-key (EPK) window. The fix lives in the query driver's dataflow planner and request-header path, which sit on the hot path for all cross-partition queries.
The planner previously built request ranges solely from the backend query plan's query_ranges (which cover the whole container for an unfiltered SELECT) and never intersected them with the operation's scope. A coupled defect meant that once the scope was honored, the emitted EPK window carried the point-typed x-ms-read-key-type: EffectivePartitionKey header, which the gateway rejects with HTTP 400.
Changes:
- Planner: new
clip_to_operation_scope/scope_covers_full_keyspacehelpers intersect each query-plan range withCosmosOperation::target()in bothplan_freshandplan_resume_from_saved_snapshot(full-key-space scope imposes no restriction; ranges fully outside the scope are dropped). - Header:
apply_headersnow emitsx-ms-read-key-type: EffectivePartitionKeyRange(matching .NET/Java) only when an EPK bound is actually written, so whole-partition ranges emit neither the bound nor the header. - Tests: two new planner unit tests, a corrected/added
apply_headersunit test, and a new emulator end-to-end regression test.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
sdk/cosmos/azure_data_cosmos_driver/src/driver/dataflow/planner.rs |
Adds scope-clipping helpers and applies them in both cross-partition planners; adds two unit tests. |
sdk/cosmos/azure_data_cosmos_driver/src/driver/pipeline/operation_pipeline.rs |
Emits the correct EffectivePartitionKeyRange read-key-type only when an EPK bound is written; updates/adds unit tests. |
sdk/cosmos/azure_data_cosmos/tests/emulator_tests/cosmos_scope_bug.rs |
New end-to-end emulator regression test asserting scoped windows return exactly the scoped subset. |
sdk/cosmos/azure_data_cosmos/tests/emulator_tests/mod.rs |
Registers the new test module. |
sdk/cosmos/azure_data_cosmos/CHANGELOG.md |
Adds a user-facing bug-fix entry (contains a #PLACEHOLDER PR link). |
sdk/cosmos/azure_data_cosmos_driver/CHANGELOG.md |
Adds a driver-level bug-fix entry (contains a #PLACEHOLDER PR link). |
|
|
||
| ### Bugs Fixed | ||
|
|
||
| - Fixed cross-partition queries scoped to a sub-partition feed range (`ContainerClient::query_items` with `FeedScope::range(..)`) ignoring the scope and scanning the whole container, returning documents outside the requested effective-partition-key window. The planner now clips each query-plan range to the operation's scope, and the SDK tags the resulting interior `x-ms-start-epk`/`x-ms-end-epk` window with `x-ms-read-key-type: EffectivePartitionKeyRange` (the value .NET and Java pair with an EPK window) instead of the point value `EffectivePartitionKey`, which the gateway rejected with HTTP 400. ([#PLACEHOLDER](https://github.com/Azure/azure-sdk-for-rust/pull/PLACEHOLDER)) |
|
|
||
| ### Bugs Fixed | ||
|
|
||
| - Fixed the cross-partition query planner ignoring an operation's scope feed range. `plan_fresh` and `plan_resume_from_saved_snapshot` built request ranges solely from the backend query plan's `query_ranges` (which cover the whole container for an unfiltered `SELECT`) and never intersected them with `CosmosOperation::target()`, so a query scoped to a sub-partition effective-partition-key window fanned out over the entire container instead of the requested slice. The planner now clips each query-plan range to the operation's scope (dropping ranges that fall entirely outside it; a full-key-space scope imposes no restriction). Additionally, an emitted `x-ms-start-epk`/`x-ms-end-epk` window is now tagged `x-ms-read-key-type: EffectivePartitionKeyRange` (matching .NET/Java) and only when a bound is actually written; previously the point value `EffectivePartitionKey` was sent alongside the range window, which the gateway rejected with a bare HTTP 400. ([#PLACEHOLDER](https://github.com/Azure/azure-sdk-for-rust/pull/PLACEHOLDER)) |
| mod cosmos_proxy; | ||
| mod cosmos_query; | ||
| mod cosmos_response_metadata; | ||
| mod cosmos_scope_bug; |
…y_space Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ame test file - Add plan_resume_from_saved_snapshot scope-clip unit test (finding Azure#3): the resume path shares the same clip call as plan_fresh but had no dedicated coverage. - Add a zero-doc gap window to the emulator e2e test (finding Azure#4): a window entirely between two adjacent EPKs must return no documents, guarding the inclusive/exclusive lower-bound behavior. - Rename cosmos_scope_bug.rs -> cosmos_query_feed_range_scope.rs to match the feature-oriented cosmos_* naming convention (finding Azure#5). Note: the point-range collapse (finding Azure#1, WHERE pk = @A scoped to a FeedRange window) is deferred — a clip-only guard is insufficient because plan_fresh and plan_resume both re-intersect against the resolved partition with the same helper; a complete point-aware path belongs with the point-ops work in Azure#4638. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Closing this as superseded — the two bugs it fixed are now covered upstream:
The one piece not covered by either — a standard-gateway end-to-end regression test for feed-range-scoped cross-partition queries — I've offered on #4729 so that coverage isn't lost. Thanks @analogrelay for the original review that surfaced the stale-override discussion, and @simorenoh / @tvaron3 for landing the broader fixes. |
Fixes #4725
Problem
A cross-partition query scoped to a sub-partition feed range
(
ContainerClient::query_itemswithFeedScope::range([X, Y))) ignored thescope and scanned the whole container, returning documents outside the requested
effective-partition-key window.
Concrete example — a container with docs on keys
"doc-000"…"doc-039", andan interior EPK window that covers 38 of the 40 keys:
See #4725 for the full repro and analysis.
Root cause
plan_fresh/plan_resume_from_saved_snapshot(driver/dataflow/planner.rs)built request ranges solely from the backend query plan's
query_ranges(whichcover the whole container for an unfiltered
SELECT) and never intersected themwith
CosmosOperation::target()(the scope feed range).A coupled defect surfaced once the scope is honored:
apply_headerstagged anemitted
start/end-epkwindow with the point valuex-ms-read-key-type: EffectivePartitionKey, which the gateway rejects with abare HTTP 400.
Fix
clip_to_operation_scopehelper intersects each query-planrange with the operation's scope before topology resolution. A full-key-space
scope imposes no restriction; a range entirely outside the scope is skipped.
Applied in both
plan_freshandplan_resume_from_saved_snapshot.apply_headersnow emitsx-ms-read-key-type: EffectivePartitionKeyRange(matching .NET/Java) and onlywhen an EPK bound is actually written; a whole-partition feed range emits
neither an EPK bound nor the read-key-type header.
Tests
scopes_cross_partition_query_to_requested_feed_range(clips to the window) and
full_container_scope_does_not_restrict_cross_partition_query(no restriction).apply_headersunit tests: read-key-type value corrected; newwhole-partition-omits-headers test.
(
emulator_tests/cosmos_scope_bug.rs): seeds one doc per PK, computes EPKslocally, and asserts interior windows return exactly the scoped subset. Run with:
RUSTFLAGS='--cfg test_category="emulator"' cargo test -p azure_data_cosmos --test emulator --features "key_auth fault_injection" feed_range_scopeValidation
cargo test -p azure_data_cosmos_driver --lib— 1950 pass (incl. 5 new).cargo clippy --all-targetsclean;cargo docclean; emulator test binary builds.