[feature](runtime filter) Support single-column runtime filter bucket pruning#65837
[feature](runtime filter) Support single-column runtime filter bucket pruning#65837HappenLee wants to merge 1 commit into
Conversation
… pruning
### What problem does this PR solve?
Issue Number: None
Related PR: None
Problem Summary: Runtime filters could prune table partitions but still created and scheduled scanners for every hash-distributed bucket. Add FE eligibility metadata for direct targets on a single HASH distribution column and use exact IN values in BE to compute Doris CRC bucket indexes. Initial and late filters skip nonmatching tablet scanners. Composite distribution and non-invertible Bloom filters conservatively fall back.
### Release note
Support runtime-filter bucket pruning for exact filters on single-column HASH-distributed OLAP scans. It can be disabled with enable_runtime_filter_bucket_prune.
### Check List (For Author)
- Test: Unit Test and Regression test
- FE unit test: RuntimeFilterBucketPruneClassifierTest (6 tests)
- BE unit test: RuntimeFilterBucketPrunerTest (5 tests)
- Regression test: query_p0/runtime_filter/rf_bucket_pruning
- Build: ./build.sh --fe and ./build.sh --be -j 48
- Static analysis: run-clang-tidy.sh on modified C++ translation units
- Behavior changed: Yes. Eligible runtime filters skip nonmatching hash buckets.
- Does this need documentation: No
|
Thank you for your contribution to Apache Doris. Please clearly describe your PR:
|
|
/review |
|
Codex automated review failed and did not complete. Error: You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at Jul 25th, 2026 6:03 AM. Please inspect the workflow logs and rerun the review after the underlying issue is resolved. |
morrySnow
left a comment
There was a problem hiding this comment.
Thanks for this PR! The implementation is well-structured and integrates cleanly with the existing runtime-filter infrastructure. Below is a summary of my overall findings; detailed inline comments follow.
Overview: This PR adds the ability for exact IN runtime filters on single-column HASH-distributed OLAP tables to skip scanning tablets whose bucket index doesn't match any filter value. The FE classifier determines eligibility, and the BE pruner materializes filter values into hashes and computes which buckets to skip. The feature is gated behind enable_runtime_filter_bucket_prune (default true).
Strengths:
- Clean separation between FE eligibility classification and BE pruning logic
- Good test coverage (FE unit tests, BE unit tests, regression test)
- Proper concurrency handling with shared_mutex
- Conservatively falls back when prerequisites aren't met (Bloom, composite hash, etc.)
- Efficient: caches selected buckets by bucket_num to avoid recomputation
Key issues to address:
- Asymmetric column comparison in
sameColumn()— uses different name methods for target vs distribution columns, which could miss pruning opportunities - NULL handling in
materialize_hashes— inserting a default value for NULL adds a spurious hash that prevents optimal pruning - A few minor style/convention items noted inline
morrySnow
left a comment
There was a problem hiding this comment.
Inline comments on specific code locations (see the overall review for summary).
| && targetUniqueId == distributionUniqueId) { | ||
| return true; | ||
| } | ||
| return targetColumn.tryGetBaseColumnName().equalsIgnoreCase(distributionColumn.getName()); |
There was a problem hiding this comment.
Issue: Asymmetric column-name comparison may miss pruning opportunities.
The comparison uses targetColumn.tryGetBaseColumnName() on the target side but distributionColumn.getName() on the distribution side. These methods can return different representations of the same column name:
tryGetBaseColumnName()may strip expression-manipulation prefixes/suffixes.getName()returns the raw column name from the catalog definition.
If the target column's name was rewritten (e.g., with a qualifier prefix) while the distribution column's raw name doesn't match the stripped version, this comparison could incorrectly return false, disabling bucket pruning for an eligible scan.
Suggestion: Use the same name-retrieval method on both sides, or compare by getUniqueId() directly as the primary check, falling back to name comparison only when unique IDs are unset.
| if (is_string_type(primitive_type)) { | ||
| const auto* string_value = reinterpret_cast<const StringRef*>(value); | ||
| column->insert_data(string_value->data, string_value->size); | ||
| } else { |
There was a problem hiding this comment.
Nit: insert_data(value, 0) relies on an implicit convention.
For non-string fixed-length types (INT, BIGINT, etc.), the second argument 0 is passed to insert_data(). ColumnVector<T>::insert_data ignores the length parameter (marked /*length*/), reading exactly sizeof(T) bytes via unaligned_load. This is correct but the convention is not obvious to a reader.
Suggestion: Add a brief comment:
// length=0 is unused for fixed-length types; ColumnVector::insert_data ignores it.
column->insert_data(reinterpret_cast<const char*>(value), 0);| } | ||
| iter->next(); | ||
| } | ||
| if (hybrid_set->contain_null() && data_type->is_nullable()) { |
There was a problem hiding this comment.
Issue: Inserting a default value for NULL adds a spurious hash that reduces pruning effectiveness.
When hybrid_set->contain_null() is true, column->insert_default() adds a default value whose hash is computed and included in the selected-bucket set. Since NULL IN (...) evaluates to NULL (unknown), not true, no bucket should be kept open solely for the NULL element in the IN list. Adding the default value's hash may keep an extra bucket open that could otherwise be pruned.
While this is safe (it is conservative — it only fails to prune a prunable bucket, never incorrectly prunes a needed bucket), it represents a missed pruning opportunity.
Suggestion: Either:
- Skip the
insert_default()call entirely — NULL does not match any row, so its hash should not prevent any bucket from being pruned. - Or add a comment explaining that including the NULL hash is intentionally conservative and why.
What problem does this PR solve?
Issue Number: None
Related PR: None
Problem Summary: Runtime filters could prune table partitions but still created and scheduled scanners for every hash-distributed bucket. Add FE eligibility metadata for direct targets on a single HASH distribution column and use exact IN values in BE to compute Doris CRC bucket indexes. Initial and late filters skip nonmatching tablet scanners. Composite distribution and non-invertible Bloom filters conservatively fall back.
Release note
Support runtime-filter bucket pruning for exact filters on single-column HASH-distributed OLAP scans. It can be disabled with enable_runtime_filter_bucket_prune.
Check List (For Author)