gortiz commented on code in PR #19120:
URL: https://github.com/apache/pinot/pull/19120#discussion_r4376711023


##########
pinot-core/src/main/java/org/apache/pinot/core/operator/InstanceResponseOperator.java:
##########
@@ -116,14 +124,14 @@ protected BaseResultsBlock getBaseBlock() {
 
     BaseResultsBlock resultsBlock = getCombinedResults();
 
-    long mainThreadCpuTimeNs = resourceSnapshot.getCpuTimeNs();
-    long mainThreadMemAllocatedBytes = resourceSnapshot.getAllocatedBytes();
-
-    long totalWallClockTimeNs = System.nanoTime() - startWallClockTimeNs;
+    _mainThreadCpuTimeNs += resourceSnapshot.getCpuTimeNs();

Review Comment:
   This is more accurate than before: the old code kept only the last call, 
which on a streaming response is the terminal metadata block, so main-thread 
CPU and allocations were close to zero and system activities came out as 0. The 
only caveat is that the summed wall clock excludes time blocked in the send, so 
system activities is now a lower bound, which is still better than 0. Since 
this applies to every streaming combine, please mention it in the description.



##########
pinot-spi/src/main/java/org/apache/pinot/spi/utils/CommonConstants.java:
##########
@@ -1698,6 +1711,53 @@ public enum AndRestrictionPushdownMode {
     // Sequential combine utilizes N + 1 threads, N for processing and 1 for 
merge. Pair-wise only uses N.
     public static final int 
DEFAULT_SORT_AGGREGATE_SEQUENTIAL_COMBINE_NUM_SEGMENTS_THRESHOLD =
         Runtime.getRuntime().availableProcessors();
+
+    /// Selects the streaming k-way-merge selection ORDER BY combine
+    /// ([Broker.Request.QueryOptionKey#SORTED_SELECTION_MERGE_MODE]).
+    ///
+    /// The merge runs single-threaded on the consumer thread, so it trades 
`maxExecutionThreads` worth of parallel
+    /// segment processing for the ability to emit sorted blocks 
incrementally. That trade only pays off when the
+    /// segments are physically sorted on the leading ORDER BY column, since 
an unsorted child must do a full scan
+    /// plus top-K synchronously before the merge can take its first row.
+    public enum SortedSelectionMergeMode {
+      /// Never use the streaming merge. Selection ORDER BY keeps its existing 
combine operators.
+      OFF,
+      /// Always use the streaming merge, including when the leading ORDER BY 
expression is not an identifier and no
+      /// segment is sorted. Children then materialize their top-K eagerly and 
the merge is strictly slower than
+      /// [#OFF]; intended for benchmarking and A/B, not for production 
traffic.
+      ON,
+      /// Use the streaming merge only when at least
+      /// 
[Broker.Request.QueryOptionKey#SORTED_SELECTION_MERGE_AUTO_MIN_SORTED_RATIO] of 
the queried segments are
+      /// physically sorted on the leading ORDER BY column. Decided from 
segment metadata alone, without acquiring
+      /// any segment.
+      AUTO

Review Comment:
   Could we say here that AUTO is experimental for now, like ON? The default 
ratio is unmeasured and the heuristic ignores segment size and overlap (tracked 
in #19924).



##########
pinot-core/src/main/java/org/apache/pinot/core/operator/query/StreamingSelectionOrderByOperator.java:
##########
@@ -0,0 +1,620 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pinot.core.operator.query;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.CaseFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.PriorityQueue;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import org.apache.pinot.common.request.context.ExpressionContext;
+import org.apache.pinot.common.request.context.OrderByExpressionContext;
+import org.apache.pinot.common.utils.DataSchema;
+import org.apache.pinot.common.utils.HashUtil;
+import org.apache.pinot.core.common.BlockValSet;
+import org.apache.pinot.core.common.Operator;
+import org.apache.pinot.core.common.RowBasedBlockValueFetcher;
+import org.apache.pinot.core.operator.BaseOperator;
+import org.apache.pinot.core.operator.BaseProjectOperator;
+import org.apache.pinot.core.operator.BitmapDocIdSetOperator;
+import org.apache.pinot.core.operator.ColumnContext;
+import org.apache.pinot.core.operator.ExecutionStatistics;
+import org.apache.pinot.core.operator.ExplainAttributeBuilder;
+import org.apache.pinot.core.operator.ProjectionOperator;
+import org.apache.pinot.core.operator.ProjectionOperatorUtils;
+import org.apache.pinot.core.operator.blocks.ValueBlock;
+import org.apache.pinot.core.operator.blocks.results.SelectionResultsBlock;
+import org.apache.pinot.core.operator.transform.TransformOperator;
+import 
org.apache.pinot.core.operator.transform.function.TransformFunctionFactory;
+import org.apache.pinot.core.query.request.context.QueryContext;
+import org.apache.pinot.core.query.selection.SelectionOperatorUtils;
+import org.apache.pinot.core.query.utils.OrderByComparatorFactory;
+import org.apache.pinot.segment.spi.IndexSegment;
+import org.apache.pinot.segment.spi.datasource.DataSource;
+import org.apache.pinot.spi.query.QueryScanCostContext;
+import org.roaringbitmap.RoaringBitmap;
+
+
+/// Lazy, incremental selection ORDER BY operator for segments that are 
physically sorted on the first order-by column.
+///
+/// Unlike [SelectionOrderByOperator] (which materializes the segment's whole 
top-K in a single block) this
+/// operator emits one globally-sorted [SelectionResultsBlock] per 
[#getNextBlock()] call and returns
+/// `null` when the segment is exhausted, so that a downstream k-way-merge 
combine operator can pull from many
+/// segments lazily and stop early. It relies on the underlying project 
operator iterating the first order-by column in
+/// the query order (the caller must guarantee 
`projectOperator.isCompatibleWith(DocIdOrder.fromAsc(asc))`).
+///
+/// Every instance emits at least one block before `null`: a segment matching 
no rows emits a single empty block
+/// carrying the [DataSchema], so consumers never have to reconstruct a schema 
the segment already knows.
+///
+/// It runs in one of two emission modes:
+///
+/// - **No tail to sort** (`numSortedExpressions == numOrderByExpressions`, 
e.g. `ORDER BY sorted`):
+///   rows already arrive from the project operator in final order, so each 
call emits the next project block
+///   (trimmed to the remaining `limit + offset` budget).
+/// - **Tail to sort** (`numSortedExpressions < numOrderByExpressions`, e.g.
+///   `ORDER BY sorted, other`):
+///   each call reads forward until a value of the sorted prefix changes (a 
"run": rows that tie on every sorted
+///   order-by column), retains the run's top rows by the unsorted tail, as 
many as the remaining `limit + offset`
+///   budget allows, and emits them sorted. This bounds the in-memory run 
buffer to `limit + offset` rows even when
+///   the sorted prefix is near-constant (very low cardinality).
+///
+/// Like [SelectionOrderByOperator] it preserves the two-phase projection 
optimization: when there are output

Review Comment:
   Nit: this operator always does its own two-phase fetch and ignores 
lateMaterializeSortedOrderBy. That's fine, but a sentence here would avoid 
confusion now that #19879 added that option. Also, with #19879 there are three 
copies of the selected-row payload fetch, so the shared helper in #19923 could 
start from LinearSelectionOrderByOperator's version.



-- 
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