Conversation
Add the `datafusion_physical_expr::filter_stats` module with the shared primitives that adaptive filter code uses to measure filters at runtime: - `Clock`: a monotonic clock in nanoseconds that tests can replace. `SystemClock` is the real clock. `ManualClock` moves only when a test moves it, thus decisions that use time are deterministic in tests. - `FilterCost`: the rows in, the rows out and the evaluation time of one filter, and the derived cost for each row and rows removed for each nanosecond. - `duration_nanos`: a `Duration` in nanoseconds, saturated to `u64::MAX`. No code uses the module yet, thus behavior does not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add adaptive conjunct reordering behind `datafusion.execution.adaptive_filter_reordering` (default `false`): each stream measures its conjuncts over a warm-up, ranks them by rows dropped per nanosecond, and adopts a new order (built as a plain `BinaryExpr` AND chain) only if the estimated cost, using `BinaryExpr`'s pre-selection rule, is at least 5% lower. Predicates with volatile expressions are never reordered. The decision is made one time for each stream, and streams do not share state. The measurements use `Clock` and `FilterCost` of `datafusion_physical_expr::filter_stats`, so tests use a `ManualClock`. `PRE_SELECTION_THRESHOLD` is exported (doc-hidden) so that the cost model uses the same rule as `BinaryExpr`. New metric: `adaptive_reorders`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thank you for opening this pull request! Reviewer note: cargo-semver-checks reported the current version number is not SemVer-compatible with the changes in this pull request (compared against the base branch). Details |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #25721 +/- ##
========================================
Coverage 82.49% 82.49%
========================================
Files 1140 1142 +2
Lines 438781 439571 +790
Branches 438781 439571 +790
========================================
+ Hits 361965 362633 +668
- Misses 54938 55019 +81
- Partials 21878 21919 +41 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
adriangb
added a commit
to pydantic/datafusion
that referenced
this pull request
Sep 26, 2026
The post-scan filter of the Parquet scan uses the pre-selection threshold of `AND` to decide when to compact its working batch (next commit). This is the same change as in the FilterExec reordering PR (apache#25721), which models the same rule. When one of the two PRs merges, this commit becomes empty in the other. PR: apache#25727 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat: adaptive conjunct reordering in FilterExec
Which issue does this PR close?
filter_statsprimitives. Adaptive (runtime, stats-based) conjunct reordering for FilterExec #22698 stays open for its review history.main. Two commits. The first commit (filter_stats) is also the first commit of feat: add OptionalFilterGate to pause optional filters that cost more than they save #25674. When one of the two PRs merges, the copy in the other PR becomes empty on rebase.Rationale for this change
The order of the conjuncts of an
ANDpredicate is important.BinaryExprevaluates the right side only on the rows that the left side keeps, if the left side keeps few rows. A selective, cheap conjunct written last makes the other conjuncts run on all rows.This PR starts from the review of #22698:
BinaryExprAND chainBinaryExprpre-selectionPRE_SELECTION_THRESHOLD(nowpub,#[doc(hidden)])Clock(ManualClockin tests)unwrap, noUInt32ArraydowncastWhat changes are included in this PR?
datafusion.execution.adaptive_filter_reordering(experimental, defaultfalse):graph LR W["8 warm-up batches:<br/>measure each conjunct"] --> R["rank by rows removed per ns"] R --> D{"estimated cost ≥ 5% lower?"} D -- yes --> N["new BinaryExpr AND chain"] D -- no --> O["keep the original predicate"]filter_stats::{FilterCost, Clock, SystemClock, ManualClock}(first commit)filter/conjunct_order.rsConjunctOrder: warm-up measurement, ranking, cost estimate with theBinaryExprpre-selection ruleFilterExecConjunctOrderfor each stream when the option is onadaptive_reordersVolatile predicates are never reordered. A fallible conjunct can see different rows after a reorder (for example
b <> 0 AND 1 / b > 2); the config documentation says so.There are no optional-filter concepts in this PR.
What is the testing strategy for this PR?
conjunct_order.rsANDcannot pre-select, nulls prevent a reorder, right-nested result, empty batches are not measured, volatile predicates are not reordered, the pre-selection model matchesBinaryExprFilterExecadaptive_filter_reordering_returns_same_rows(same rows, metric present only with two or more conjuncts)adaptive_filter_reordering.sltfilter_statsAre there any user-facing changes?
One new config option (default off) and one new metric. The default behavior does not change.
🤖 Generated with Claude Code