Jackie-Jiang commented on code in PR #19539:
URL: https://github.com/apache/pinot/pull/19539#discussion_r4201818348


##########
pinot-segment-spi/src/main/java/org/apache/pinot/segment/spi/partition/PartitionFunction.java:
##########
@@ -58,6 +62,13 @@ default List<String> getNames() {
   /// @return Number of possible partitions.
   int getNumPartitions();
 
+  /// Whether query-local predicate preparation is useful for this function. 
Functions whose instances cannot be
+  /// compared cheaply across segments can return false to use the original 
per-segment pruning path.
+  default boolean supportsPartitionIdPreparation() {
+    return true;

Review Comment:
   Can we default this to `false` and explicitly enable preparation for 
implementations with suitable value equality? Existing plugins that inherit 
`Object.equals` automatically take the prepared path, but 
`SegmentPartitionUtils`/`PartitionFunctionFactory` construct a distinct 
function instance per segment, so their prepared predicates are never reused. 
For S >= 32 segments and an IN list of V values whose first value matches every 
segment, the legacy path performs S partition calculations, while this path 
performs S * V and retains S candidate sets until the prune call finishes. 
Making the capability opt-in avoids this CPU/allocation regression for existing 
plugins.



##########
pinot-broker/src/main/java/org/apache/pinot/broker/routing/segmentpruner/SinglePartitionColumnSegmentPruner.java:
##########
@@ -152,4 +237,107 @@ private boolean isPartitionMatch(Expression 
filterExpression, SegmentPartitionIn
         return true;
     }
   }
+
+  /// Computes conservative candidate IDs for the whole predicate. Null 
represents every partition.
+  /// Partition columns are single-valued, so a row satisfying AND must belong 
to every child's candidate set.
+  @Nullable
+  private IntSet preparePartitionIds(Expression expression, PartitionFunction 
partitionFunction) {
+    Function function = expression.getFunctionCall();
+    FilterKind kind = FilterKind.valueOf(function.getOperator());
+    List<Expression> operands = function.getOperands();
+    switch (kind) {
+      case AND: {
+        IntSet ids = null;
+        for (Expression child : operands) {
+          IntSet childIds = preparePartitionIds(child, partitionFunction);
+          if (childIds != null) {
+            if (ids == null) {
+              ids = childIds;
+            } else {
+              // Singleton leaves are immutable; composite and IN sets can be 
intersected in place.
+              if (!(ids instanceof IntOpenHashSet)) {
+                ids = new IntOpenHashSet(ids);
+              }
+              ids.retainAll(childIds);
+            }
+          }
+        }
+        return ids;
+      }
+      case OR: {
+        IntSet ids = new IntOpenHashSet();
+        boolean matchesAll = false;
+        for (Expression child : operands) {
+          IntSet childIds = preparePartitionIds(child, partitionFunction);
+          if (childIds == null) {
+            matchesAll = true;

Review Comment:
   Can we return `null` immediately when `childIds == null`? For `otherColumn = 
'x' OR memberId IN (<large list>)`, the legacy evaluator returns on the first 
child without computing any partition IDs. Here we set `matchesAll` but still 
hash the entire IN list and build a set that is ultimately discarded. An 
unrestricted child already determines the OR's candidate set, and later 
siblings were unreachable in the legacy evaluator, so stopping here preserves 
its short-circuit/error behavior. This also removes the redundant `matchesAll` 
flag. A counting-function test with an unrelated first child and a large IN 
sibling would cover it.



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