8000 fix(api): validate scroll searches the way SearchRequest#validate does by amartya1523 · Pull Request #2915 · xerj-org/xerj · GitHub
[go: up one dir, main page]

Skip to content

fix(api): validate scroll searches the way SearchRequest#validate does - #2915

Merged
xerj-org merged 2 commits into
xerj-org:mainfrom
amartya1523:fix/scroll-request-validation
Nov 4, 2026
Merged

xerj-org merged 2 commits into
xerj-org:mainfrom
amartya1523:fix/scroll-request-validation

Conversation

@amartya1523
Copy link
Copy Markdown
Contributor

AI-assisted change, run and reviewed by @amartya1523.

What this changes, and why

ES refuses these scroll searches in SearchRequest#validate, before any shard runs, with one 400 action_request_validation_exception:

request with ?scroll= ES message
size: 0 (body or ?size=0) [size] cannot be [0] in a scroll context
?request_cache=true [request_cache] cannot be used in a scroll context
from > 0 using [from] is not allowed in a scroll context
a rescore using [rescore] is not allowed in a scroll context
a search_after [search_after] cannot be used in a scroll context

XERJ ran each one and opened a scroll context. size: 0 opened a context that can never page. from: 1 returned the very document it asked to skip, because the snapshot setup in search_impl resets from to 0 without first looking at what the caller sent. Two catch-lenient.txt lines are this case (scroll/10_basic.yml · Scroll cannot used the request cache, · Scroll with size 0).

Fix:

  • scroll_request_validation_errors collects the messages in ES's order. search_impl answers them before the open-scroll cap check, so a refused request never leaves a context behind.
  • Several errors are numbered together (Validation Failed: 1: …;2: …;), as ES does.
  • The existing helper that renders this envelope (api_key_validation_error, one caller) is renamed action_request_validation_error, since nothing in it is api-key specific.

Not mirrored, on purpose:

Evidence

Wire, node built from main b78004832 vs this branch (one doc in t):

POST /t/_search?scroll=1m {"size":0}                      main 200 + _scroll_id -> branch 400 "Validation Failed: 1: [size] cannot be [0] in a scroll context;"
POST /t/_search?scroll=1m&request_cache=true {}           main 200 + _scroll_id -> branch 400 "... [request_cache] cannot be used in a scroll context;"
POST /t/_search?scroll=1m {"from":1}                      main 200, hits: [doc 1] -> branch 400 "... using [from] is not allowed in a scroll context;"
POST /t/_search?scroll=1m {"sort":["n"],"search_after":[0]}   main 200 -> branch 400
POST /t/_search?scroll=1m {"rescore":{...}}               main 200 -> branch 400
POST /t/_search?scroll=1m&request_cache=true {"size":0}   branch 400 "Validation Failed: 1: [size] cannot be [0] in a scroll context;2: [request_cache] cannot be used in a scroll context;"
POST /t/_search?scroll=1m {"collapse":{"field":"s"}}      200 on both

New test module scroll_request_validation_tests (es_compat.rs), run against main first:

# main
test es_compat::scroll_request_validation_tests::scroll_rejects_what_search_request_validate_rejects ... FAILED
  left: 200  right: 400
test es_compat::scroll_request_validation_tests::the_same_options_without_scroll_still_answer ... ok

# this branch
test es_compat::scroll_request_validation_tests::scroll_rejects_what_search_request_validate_rejects ... ok
test es_compat::scroll_request_validation_tests::the_same_options_without_scroll_still_answer ... ok

Checks

  • cargo fmt --all
  • Scoped build of the touched crate, quick profile (cargo build --profile quick -p xerj-server, per docs/FAST_BUILDS.md). Not --release; see Not run.
  • cargo test --profile quick -p xerj-api --lib: 417 passed, 0 failed (all three sibling fixes applied)
  • ES-YAML suite at 0 failed: 1453 passed · 0 failed · 10 skipped · 1463 total, 65 cases catch-lenient (was 74), no stale-entry notes. Run with es-yaml-runner --dir tests/es-compat-yaml/yaml against a quick node built from main b78004832 plus this PR and its two siblings (fix(api): reject avg/sum/min/max/stats on a non-numeric field like ES #2913, fix(api): answer 404 when a clear-scroll frees nothing #2914).
  • YAML case: the two scroll/10_basic.yml cases now enforce it; their catch-lenient lines are deleted.
  • Integration tests run (each alone, quick): collapse_scroll_page_parity, collapse_scroll_source_false_repro, collapse_scroll_snapshot_version_torn_read, scroll_snapshot_cap_is_documented, scroll_source_suppression_parity, scroll_seq_no_torn_read, scroll_multi_index_identity, scroll_alias_multi_index_cap, search_scroll_*, slice_spec_is_validated and the rest of the 31: all pass, except the replica-timing flake in cluster_metadata_replicates, which does not scroll.
  • Docs: none describe accepting these.

Not run: --release builds and local clippy, for disk reasons (the machine is at 99%). The quick profile from FAST_BUILDS.md was used instead.

Merge note: this and #2914 both delete adjacent lines in catch-lenient.txt. Whichever lands second needs a trivial rebase, and I'll do it.

Provenance

  • Written by: AI-assisted, run and reviewed by @amartya1523
  • Verified: the commands and outputs above, on macOS 26 (aarch64), quick profile, based on main b78004832.
  • Not covered: the /{index}/_search_scroll?scroll= route opens its scroll in a separate handler and does not run this check. It sits in search_impl only, the _search?scroll= path the YAML suite uses. I can extend it there if you want both routes to agree.

Motivation:
ES refuses these scroll searches with one 400
action_request_validation_exception before any shard runs:
- `size: 0`               -> [size] cannot be [0] in a scroll context
- `?request_cache=true`   -> [request_cache] cannot be used in a scroll context
- `from > 0`              -> using [from] is not allowed in a scroll context
- a `rescore`             -> using [rescore] is not allowed in a scroll context
- a `search_after`        -> [search_after] cannot be used in a scroll context

XERJ ran each one and opened a scroll context. `size: 0` opened a
context that can never page; `from: 1` returned the very document it
asked to skip, because the scroll snapshot resets `from` to 0. Two
catch-lenient lines are this case (`scroll/10_basic.yml · Scroll
cannot used the request cache`, `· Scroll with size 0`).

Root cause:
The scroll setup in `search_impl` overwrites `size`/`from` for the
snapshot without first checking what the caller sent.

Fix:
`scroll_request_validation_errors` collects the ES messages in
SearchRequest#validate order, and `search_impl` answers them before the
open-scroll cap check, so a refused request never leaves a context
behind. Several errors are numbered together (`Validation Failed: 1:
...;2: ...;`), as ES does. The existing api-key helper that renders
this envelope is renamed `action_request_validation_error`, since
nothing in it is api-key specific.

Not mirrored on purpose: `track_total_hits: false` (XERJ forces exact
scroll totals instead), and `collapse` in a scroll, which XERJ supports
and renders on continuation pages (xerj-org#621-xerj-org#624); its catch-lenient line
(`search/110_field_collapsing.yml · field collapsing and scroll`) stays.

Evidence:
- ES-YAML suite against a node built from this change (with its two
  sibling fixes): `1453 passed · 0 failed · 10 skipped`, no stale
  catch-lenient entries; the two `scroll/10_basic.yml` lines are
  deleted.
- The collapse_scroll_* and scroll_* integration tests still pass.
Each of size 0 (body and `?size=0`), `?request_cache=true`, `from: 1`,
a rescore and a search_after answers 400 with ES's message, size 0 plus
request_cache numbers both errors, and no refused request leaves a
scroll context behind. The same options without `scroll`, a scroll with
`request_cache=false`, and a collapsed scroll still answer 200.

On main the first test fails at the first case:
  left: 200  right: 400
@cla-bot cla-bot Bot added the cla-signed label Nov 3, 2026
@xerj-org
xerj-org merged commit 4e822e1 into xerj-org:main Nov 4, 2026
22 of 23 checks passed
@xerj-org
xerj-org commented Nov 4, 2026
Copy link
Copy Markdown
Owner

Xerj Merger made the fix: validates scroll searches the way ES's SearchRequest#validate does, so invalid scroll requests are refused before a context opens. Diff reviewed, Build + Test green on ab7c697 (cargo-audit was red only from RUSTSEC-2026-0436, fixed on main by #2912). Thanks @amartya1523!

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0