You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Sandbox] Reverse indexed-parquet segment iteration for opposite-direction sorts
Mirror of `ContextIndexSearcher.shouldUseTimeSeriesDescSortOptimization` for
the indexed-parquet path. When `index.sort.field` is configured and the
query's leading ORDER BY runs counter to the catalog's stored direction,
reverse the per-shard segment iteration so a TopK above the scan pulls the
highest-priority segment first and parquet page stats prune the rest.
Reuses PR #22041's `ShardView.sort_fields` / `sort_orders` plumbing — no new
Java/FFM surface. Only the indexed scan path (`IndexedTableProvider` /
`QueryShardExec`) consumes them; the vanilla `ListingTable` path remains
covered by #22041.
## Implementation
`indexed_executor.rs`:
- `analyze_top_sort(&LogicalPlan)` walks Projection/Limit/SubqueryAlias/
Distinct/Filter to find the leading top-of-plan ORDER BY. Returns None if
the leading sort key isn't a plain `Expr::Column` (function on the sort
key breaks catalog monotonicity).
- `should_reverse_segments` triggers iff the catalog has a sort, the query's
leading sort column matches `sort_fields[0]` (case-sensitive, matching
PR #22041), and the directions differ.
- `reverse_segment_iteration_order` flips `Vec<SegmentFileInfo>` in place.
Per-segment `global_base` is deliberately NOT recomputed — see below.
- Wired into `execute_indexed_with_context_inner` after `from_substrait_plan`
decodes the LogicalPlan.
`SessionContextHandle` carries `sort_fields` / `sort_orders` cloned from
`ShardView` so both `create_session_context` and
`create_session_context_indexed` populate the indexed-path consumer.
## QTF correctness — why `global_base` is preserved on reversal
The `__row_id__` emitted by the indexed scan is `segment.global_base + position`.
QTF's fetch phase (`api::fetch_by_row_ids`) goes through a different code path
(`ShardTableProvider`, not `IndexedTableProvider`) and rebuilds segments via
`build_segments(ShardView.object_metas, ShardView.writer_generations)` — always
in catalog order. Fetch then partition_points the cached row IDs against
catalog-order `global_base`s to find the owning segment.
For the round trip to hold, query-phase IDs must match catalog-order bases.
`reverse_segment_iteration_order` therefore flips iteration order ONLY —
each segment retains its catalog-order `global_base`. Reversal becomes purely
a scheduling decision; the row-id space is unchanged.
## Tests
Rust unit tests in `indexed_executor::tests`:
- `analyze_top_sort` (top-level desc, descends through Limit/Projection,
None for no-sort, None for function sort key).
- `should_reverse_segments` (matching field opposite direction, matching
same direction, catalog desc query asc, no query sort, no catalog sort,
non-leading catalog field, case-sensitive name).
- `reverse_segments_preserves_global_base`, `reverse_segments_empty_is_noop`,
`reverse_segments_single_keeps_its_base`.
Rust e2e tests in `indexed_table/tests_e2e/sort_reverse_row_id.rs`:
- `three_segments_row_ids_invariant_under_reversal`,
`four_segments_row_ids_invariant_under_reversal` — pin the load-bearing
invariant: same shard-global `__row_id__` set whether iteration is
natural or reversed.
Java IT in `sandbox/qa/analytics-engine-coordinator/.../SortReverseQtfIT.java`
(parquet+lucene composite, 2 shards, 3 flushes → 2-4 segments per shard):
- `testReversalTriggers_orderByTsDescLimit_correctResults` — captures the
reversal log line via `MockLogAppender` on `RustLoggerBridge` AND asserts
result correctness (top-k by ts DESC).
- `testNoReversal_orderByTsAscLimit_correctResults` — `UnseenEventExpectation`
proves reversal does NOT fire; results still correct.
- `testReversal_keywordFilterDelegationPath_correctResults` — keyword filter
shipped to Lucene perf peer + reversal; verifies QTF row-id round-trip
composes with delegation.
Signed-off-by: Arpit Bandejiya <abandeji@amazon.com>
0 commit comments