Skip to content

cosmos: scope cross-partition queries to their requested feed range#4726

Closed
ananth7592 wants to merge 4 commits into
Azure:mainfrom
ananth7592:fix/cross-partition-query-scope-ignored
Closed

cosmos: scope cross-partition queries to their requested feed range#4726
ananth7592 wants to merge 4 commits into
Azure:mainfrom
ananth7592:fix/cross-partition-query-scope-ignored

Conversation

@ananth7592

Copy link
Copy Markdown
Member

Fixes #4725

Problem

A cross-partition query scoped to a sub-partition feed range
(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.

Concrete example — a container with docs on keys "doc-000""doc-039", and
an interior EPK window that covers 38 of the 40 keys:

let window = FeedRange::new(min_epk, max_epk)?; // interior slice [X, Y)
let mut items = container
    .query_items::<Doc>("SELECT * FROM c", FeedScope::range(window), None)
    .await?;
// Expected 38 documents; before this fix, all 40 came back (full scan).

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 (which
cover the whole container for an unfiltered SELECT) and never intersected them
with CosmosOperation::target() (the scope feed range).

A coupled defect surfaced once the scope is honored: apply_headers tagged an
emitted start/end-epk window with the point value
x-ms-read-key-type: EffectivePartitionKey, which the gateway rejects with a
bare HTTP 400.

Fix

  • Planner: new clip_to_operation_scope helper intersects each query-plan
    range 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_fresh and plan_resume_from_saved_snapshot.
  • Header: apply_headers now emits
    x-ms-read-key-type: EffectivePartitionKeyRange (matching .NET/Java) and only
    when an EPK bound is actually written; a whole-partition feed range emits
    neither an EPK bound nor the read-key-type header.

Tests

  • Planner unit 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_headers unit tests: read-key-type value corrected; new
    whole-partition-omits-headers test.
  • Emulator end-to-end regression test
    (emulator_tests/cosmos_scope_bug.rs): seeds one doc per PK, computes EPKs
    locally, 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_scope

Validation

  • cargo test -p azure_data_cosmos_driver --lib — 1950 pass (incl. 5 new).
  • cargo clippy --all-targets clean; cargo doc clean; emulator test binary builds.
  • Live-validated against a real account: control=40, wide window=38, tight window=1, no HTTP 400.

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>
Copilot AI review requested due to automatic review settings July 8, 2026 23:40
@ananth7592
ananth7592 requested a review from a team as a code owner July 8, 2026 23:40
@github-actions github-actions Bot added the Cosmos The azure_cosmos crate label Jul 8, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_keyspace helpers intersect each query-plan range with CosmosOperation::target() in both plan_fresh and plan_resume_from_saved_snapshot (full-key-space scope imposes no restriction; ranges fully outside the scope are dropped).
  • Header: apply_headers now emits x-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_headers unit 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;
ananth7592 and others added 2 commits July 8, 2026 18:34
…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>
@ananth7592

Copy link
Copy Markdown
Member Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Cosmos The azure_cosmos crate

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Cosmos: cross-partition query scoped to a FeedRange slice ignores the scope and scans the whole container

2 participants