Prune segments from column metadata instead of materializing them - #19511
Open
xiangfu0 wants to merge 1 commit into
Open
Prune segments from column metadata instead of materializing them#19511xiangfu0 wants to merge 1 commit into
xiangfu0 wants to merge 1 commit into
Conversation
…alizing them `ColumnValueSegmentPruner` asks each segment for a column's data source and then reads nothing from it but the metadata — data type, min/max, partition function. Reaching that metadata through `getDataSource` makes a segment that builds its columns lazily construct the whole index container, every index reader for the column, for a segment it is about to discard. That runs in `ValueBasedSegmentPruner#prune`, a serial loop over every segment the server holds, on the query thread. On an external table under lazy column materialization it puts a Parquet footer parse there too: on a server holding 44,780 segments the pruner alone accounted for the bulk of a query that timed out at 300 s, for a filter that matched 1,839 segments. `IndexSegment#getDataSourceMetadata(String, Schema)` names what the caller actually wants, defaulting to today's behaviour so no implementation has to change. `ImmutableSegmentImpl` answers it from column metadata: `ImmutableDataSourceMetadata` already delegates to `ColumnMetadata` and holds no readers, so it needs nothing built. A column the segment does not have still falls through to the data source, where the schema-driven default and virtual columns are created. The pruner keeps its per-segment data-source cache for mutable segments, whose metadata is not derivable without the data source. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
xiangfu0
force-pushed
the
xiangfu0/data-3221-11-metadata-only-pruning
branch
from
September 9, 2026 01:56
6f1c691 to
9a69689
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## xiangfu0/data-3221-10-physical-column-names #19511 +/- ##
=================================================================================
+ Coverage 57.80% 57.82% +0.01%
Complexity 7 7
=================================================================================
Files 2692 2692
Lines 164721 164723 +2
Branches 26757 26753 -4
=================================================================================
+ Hits 95212 95244 +32
+ Misses 61466 61435 -31
- Partials 8043 8044 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
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.
What
ColumnValueSegmentPrunerasks each segment for a column'sDataSourceand then reads nothing from it but theDataSourceMetadata— data type, min/max, partition function. Reaching that metadata throughgetDataSourceforces a segment that builds its columns lazily to construct the wholeColumnIndexContainer— every index reader for the column — for a segment it is about to discard.This adds
IndexSegment#getDataSourceMetadata(String, Schema), which names what the caller actually wants and defaults togetDataSource(column, schema).getDataSourceMetadata(), so no existing implementation has to change.ImmutableSegmentImploverrides it to answer from column metadata:ImmutableDataSourceMetadataalready delegates toColumnMetadataand holds no readers, so nothing needs building. A column the segment does not have still falls through to the data source, which is where the schema-driven default and virtual columns are created.The pruner keeps its per-segment data-source cache for mutable segments, whose metadata is not derivable without the data source.
Why
ValueBasedSegmentPruner#pruneis a serial loop over every segment the server holds, on the query thread — it implements only the two-argprune, soSegmentPruner's three-arg default silently discards theExecutorService(BloomFilterSegmentPruner, by contrast, does override it and prunes in parallel).Combined with lazy column materialization (#19477) that means the pruner materializes a column per held segment, and on a tiered-storage external table each materialization parses a Parquet footer. Measured on a production server holding 44,780 segments, this dominated a query that timed out at 300 s for a filter matching 1,839 segments — the pruner was doing tens of thousands of footer parses, in series, for segments it then discarded.
The win is not limited to lazy mode: building an index container to read min/max is wasted work in any configuration.
Tests
ImmutableSegmentImplTest#testDataSourceMetadataDoesNotMaterializeTheColumnbuilds a lazy segment with a mockedColumnMaterializer, reads the metadata, and assertsverifyNoInteractions(materializer)— then shows that asking for the data source does materialize, and that the two agree. It fails when the override is removed.51 pruner tests in pinot-core and 35 segment tests in pinot-segment-local pass; spotless, checkstyle and license clean.
Stack
Part 11, based on #19486. Review only this part's own commit; the earlier parts account for the rest of the diff.
🤖 Generated with Claude Code