xiangfu0 commented on code in PR #19539:
URL: https://github.com/apache/pinot/pull/19539#discussion_r4190949930
##########
pinot-broker/src/main/java/org/apache/pinot/broker/routing/segmentpruner/SinglePartitionColumnSegmentPruner.java:
##########
@@ -152,4 +223,106 @@ private boolean isPartitionMatch(Expression
filterExpression, SegmentPartitionIn
return true;
}
}
+
+ /// Lazily prepares only visited expressions and values. Instances belong to
one prune call, never shared by queries.
+ private final class PreparedPredicate {
+ private final Expression _expression;
+ private final PartitionFunction _partitionFunction;
+ private FilterKind _kind;
+ private List<Expression> _operands;
+ private PreparedPredicate[] _children;
+ private boolean _isPartitionPredicate;
+ private Integer _singlePartitionId;
+ private IntSet _partitionIds;
+ private int _numEvaluatedValues;
+
+ private PreparedPredicate(Expression expression, PartitionFunction
partitionFunction) {
+ _expression = expression;
+ _partitionFunction = partitionFunction;
+ }
+
+ private boolean matches(Set<Integer> partitions) {
Review Comment:
Correction to my earlier explanation: the multi-partition example
demonstrates conservative segment retention, not incorrect query results. For a
single-valued partition column, a row satisfying an AND must have one partition
ID in every child's candidate set. Intersecting those sets is safe even when a
segment contains several partitions. My claim that the intersection was
incorrect was wrong.
The updated PR now eagerly prepares the whole supported predicate once per
equivalent partition function, using intersection for AND and union for OR. It
then checks each segment against the resulting ID set. If preparation throws on
an invalid literal or expression, that function group falls back to the
original per-segment evaluator so its short-circuit/error behavior is
preserved. The tests now require pruning the impossible conjunction and cover
nested intersections, unions, and the error fallback.
##########
pinot-broker/src/main/java/org/apache/pinot/broker/routing/segmentpruner/SinglePartitionColumnSegmentPruner.java:
##########
@@ -152,4 +223,106 @@ private boolean isPartitionMatch(Expression
filterExpression, SegmentPartitionIn
return true;
}
}
+
+ /// Lazily prepares only visited expressions and values. Instances belong to
one prune call, never shared by queries.
+ private final class PreparedPredicate {
+ private final Expression _expression;
+ private final PartitionFunction _partitionFunction;
+ private FilterKind _kind;
+ private List<Expression> _operands;
+ private PreparedPredicate[] _children;
+ private boolean _isPartitionPredicate;
+ private Integer _singlePartitionId;
+ private IntSet _partitionIds;
+ private int _numEvaluatedValues;
+
+ private PreparedPredicate(Expression expression, PartitionFunction
partitionFunction) {
+ _expression = expression;
+ _partitionFunction = partitionFunction;
+ }
+
+ private boolean matches(Set<Integer> partitions) {
Review Comment:
You are right. My earlier multi-partition example demonstrated conservative
segment selection, not an incorrect result. A matching row in a single-valued
partition column has one ID that must belong to every AND candidate set. I
corrected the earlier comment.
Commit 850bad9f22 now eagerly prepares the whole supported predicate once
per equivalent function per query, intersecting AND sets and unioning OR sets.
The previous assertion requiring the impossible conjunction to be retained has
been replaced with an assertion that it is pruned. Tests also cover nested set
operations and an invalid literal after an empty intersection.
Error policy: if eager preparation throws a RuntimeException, that function
group is marked for the original per-segment evaluator for the rest of the
prune call. This preserves the old evaluator's short-circuit/error behavior
without paying repeated preparation attempts. Valid predicates use the prepared
set and singleton fast path. All 40 focused broker cases pass, along with the
downstream Custom fixture.
The bounded JMH comparison is against abbba34a77 on JDK 25: one thread, two
forks, 3 × 1-second warmups and 5 × 1-second measurements. The fixture uses
independently constructed equal 64-way Modulo functions and one partition per
segment. First-hit IN uses partition 0 throughout; other fixtures cycle through
partitions. Mean ± JMH-reported error, µs per full prune call:
| Filter | Segments | Before (µs) | Eager (µs) | Change | Interpretation |
|---|---:|---:|---:|---:|---|
| EQUALS | 256 | 1.66 ± 0.13 | 1.66 ± 0.41 | -0.2% | Inconclusive |
| EQUALS | 512 | 3.71 ± 0.11 | 3.23 ± 0.22 | -12.8% | Inconclusive |
| EQUALS | 1024 | 9.06 ± 1.05 | 9.26 ± 3.58 | +2.2% | Inconclusive |
| IN(16) | 256 | 2.66 ± 0.07 | 3.50 ± 1.04 | +31.5% | Inconclusive |
| IN(16) | 512 | 5.77 ± 0.18 | 5.23 ± 0.21 | -9.4% | Likely change |
| IN(16) | 1024 | 12.21 ± 0.34 | 10.96 ± 0.45 | -10.2% | Likely change |
| Nested AND/OR | 256 | 3.67 ± 0.09 | 3.06 ± 0.07 | -16.6% | Likely change |
| Nested AND/OR | 512 | 7.68 ± 0.41 | 5.26 ± 0.28 | -31.4% | Likely change |
| Nested AND/OR | 1024 | 16.06 ± 0.45 | 11.49 ± 1.15 | -28.5% | Likely
change |
| IN(128), first hit | 256 | 3.96 ± 0.40 | 4.90 ± 0.07 | +23.8% | Likely
change |
| IN(128), first hit | 512 | 8.35 ± 0.30 | 8.99 ± 0.16 | +7.7% | Likely
change |
| IN(128), first hit | 1024 | 19.48 ± 9.05 | 17.85 ± 1.15 | -8.3% |
Inconclusive |
The IN(16)/256 repeat was 3.97 ± 0.09 µs, so there is a real small-case cost
rather than a uniform speedup. First-hit IN also adds about 0.94/0.65 µs at
256/512. Nested preparation improves about 17/31/28% at 256/512/1024. I kept
the eager design and documented the tradeoffs and noisy controls in the PR
description. No benchmark source is committed; CI on the updated commits is
running.
--
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]