rohityadav1993 opened a new issue, #19923:
URL: https://github.com/apache/pinot/issues/19923

   Part of #18667. Follow-up to #19120.
   
   ## Context
   
   In #19120, `StreamingSelectionOrderByOperator` was added as the per-segment 
leaf of the streaming selection
   ORDER BY combine. Rows are emitted in runs. A run is a group of rows with 
equal values in the sorted prefix.
   When a selected column is not in the ORDER BY, the leaf works in two phases.
   
   ```mermaid
   flowchart LR
     G["Planner gate<br/>InstancePlanMakerImplV2<br/>(AUTO, direction)"] --> L
     subgraph L["StreamingSelectionOrderByOperator (one per segment)"]
       S["Scan in sorted order<br/>(reversed for DESC)"] --> R["Cut a 
run<br/>sort run by unsorted tail"]
       R --> F["Phase two<br/>fetchNonOrderByColumns()"]
     end
     F --> C["Streaming combine<br/>k-way merge"]
     G -. "A3" .- S
     S -. "A4" .- S
     F -. "A1, A2" .- F
   ```
   
   The items below were found in review and were deferred from #19120. Items A1 
and A2 change the same code, so
   one PR is recommended for both.
   
   ## A1. Batch small runs before the phase-two fetch
   
   Source: review comment 
https://github.com/apache/pinot/pull/19120#discussion_r4251361251
   
   One-row runs are produced when the leading sorted key is (almost) unique and 
an unsorted ORDER BY tail follows.
   Phase two is then done once per row.
   
   ```
   Today (one-row runs):
     run 1 -> build projection, fetcher, reader contexts, transform tree, 
bitmap -> fetch 1 row -> close
     run 2 -> build ...                                                         
 -> fetch 1 row -> close
     ...
     compressed raw column: the same chunk is decompressed again for each row
   
   Proposed:
     run 1 + run 2 + ... + run k (bounded batch) -> build once -> fetch k rows 
-> close
   ```
   
   - The output block size of the combine does not amortize this cost, because 
the cost is in the leaf.
   - Since #19120 (review item P2a), runs are cut on the whole sorted prefix. 
More queries reach this case, for
     example `ORDER BY day, ts` where both columns are sorted and `ts` is 
almost unique.
   - Expected change: consecutive small runs are coalesced into bounded leaf 
blocks before phase two, so the reader
     contexts are reused within each batch.
   
   ## A2. Share the selected-row projection logic with 
`SelectionOrderByOperator`
   
   Source: review body note 2, 
https://github.com/apache/pinot/pull/19120#pullrequestreview-5494854939
   
   The phase-two path duplicates logic from `SelectionOrderByOperator`:
   
   | Step | `SelectionOrderByOperator` | `StreamingSelectionOrderByOperator` |
   |---|---|---|
   | Sort doc IDs | yes | duplicated |
   | Fetch non-ORDER-BY columns | yes | duplicated |
   | Restore nulls | yes | duplicated |
   | Build the output `DataSchema` | yes | duplicated |
   | Resumable cursor, exhaustion | no | streaming only |
   
   - Expected change: a focused helper is extracted, with explicit buffer 
ownership.
   - Inheritance from the one-shot operator is not wanted: the two execution 
contracts are different.
   - The A1 batch is expected to be built on this helper.
   
   ## A3. `allowReverseOrder=true` does not guarantee a reversed scan
   
   Source: in-code TODO in `InstancePlanMakerImplV2` (cites 
https://github.com/apache/pinot/pull/19120#discussion_r3871713989)
   
   ```mermaid
   flowchart LR
     Q["DESC leading ORDER BY<br/>allowReverseOrder=true"] --> A["AUTO resolves 
ON"]
     A --> P["getSortedByProject()"]
     P -->|"doc ID set cannot be reversed:<br/>failure is swallowed"| 
U["unreversed operator returned"]
     U --> D["leaf gate declines"]
     D --> M["merge runs over materialized children"]
   ```
   
   - Result: AUTO can resolve ON while no leaf streams. The result is correct; 
only the expected benefit is lost.
   - Expected change: a predicate is added that tells, per segment and before 
the plan is built, if the segment can
     be scanned in the query's direction. AUTO is then resolved against that 
predicate.
   - This case is not tested today.
   
   ## A4. DESC with null handling: leftover reverse-bitmap cost
   
   Source: residual R3 of #19120 (partly fixed there)
   
   - Fixed in #19120: segments flagged non-null no longer build the DESC 
reverse bitmap up front.
   - Not fixed:
     - A segment that is not flagged non-null is still force-activated. The 
reverse bitmap is built for it even when
       its non-null range is outside the limit.
     - A null value on the merge frontier cannot be compared by the pruning 
check (`sortsBeyond`). So a null-bearing
       segment can keep later flagged segments from being skipped.
   - Expected change: the force-activation is limited, or the null case is made 
comparable for pruning. A test that
     counts built reverse bitmaps is recommended.
   
   ## Out of scope
   
   Combine-side follow-ups are tracked in #<ISSUE-B>.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to