Prune values across the query executor instead of on the calling thread - #19513
Open
xiangfu0 wants to merge 1 commit into
Open
Conversation
`ValueBasedSegmentPruner#prune` is a serial loop over every segment the server holds, and it runs before any per-segment parallelism starts, so on a server holding tens of thousands of segments it is a query's longest single-threaded stretch. It implemented only the two-arg `prune`, so `SegmentPruner`'s three-arg default silently discarded the `ExecutorService` it was handed. `BloomFilterSegmentPruner` already overrode that; the pruner that runs first, over the full segment set, did not. The parallel path mirrors the bloom-filter one: tasks over a strided slice of the segments, each with its own value and data-source caches — neither is thread-safe, and both are scoped to a single segment at a time regardless — run through `QueryMultiThreadingUtils.runTasksWithDeadline` so the query deadline and cancellation are honored. Below `TARGET_NUM_SEGMENTS_PER_THREAD` segments, or with no executor, it stays serial. Segments come back in a different order than they went in, which is already true of the bloom-filter pruner that runs immediately after this one. `TARGET_NUM_SEGMENTS_PER_THREAD` moves to the parent, where both pruners now read the same value instead of the child shadowing it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
xiangfu0
force-pushed
the
xiangfu0/data-3221-12-parallel-value-pruning
branch
from
September 9, 2026 01:56
8c6e534 to
785926d
Compare
xiangfu0
force-pushed
the
xiangfu0/data-3221-11-metadata-only-pruning
branch
from
September 9, 2026 01:56
6f1c691 to
9a69689
Compare
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
ValueBasedSegmentPrunernow overridesprune(List, QueryContext, ExecutorService)and prunes across the query executor when there are enough segments to be worth it.It previously implemented only the two-arg
prune, soSegmentPruner's three-arg default silently discarded the executor it was handed.BloomFilterSegmentPruneralready overrode it and pruned in parallel — the pruner that runs first, over the full segment set, did not.The parallel path mirrors the bloom-filter one: strided tasks over the segment list, each with its own
ValueCacheand data-source cache (neither is thread-safe, and both are scoped to a single segment at a time regardless), run throughQueryMultiThreadingUtils.runTasksWithDeadlineso the query deadline and cancellation are honored. BelowTARGET_NUM_SEGMENTS_PER_THREAD, or with no executor, it stays serial.TARGET_NUM_SEGMENTS_PER_THREADmoves to the parent;BloomFilterSegmentPrunerwas shadowing it with the same value.Why
This loop runs on the calling thread for every segment the server holds, before any per-segment parallelism starts. On a server holding 44,780 segments it is the longest single-threaded stretch of the query — and the default pruner chain is
ColumnValueSegmentPruner, BloomFilterSegmentPruner, SelectionQuerySegmentPruner, so the serial one runs first and on the largest input.Stacked on #19511, which removes the per-segment work in that loop (it was materializing a column to read min/max). This removes the serialisation. They are independent: either helps alone, and the combination is what makes pruning proportional to cores rather than to segments held.
Behaviour change
Segments come back in a different order than they went in. That is already true of
BloomFilterSegmentPruner, which runs immediately after this pruner in the default chain, so nothing downstream can have depended on the input order surviving pruning.Tests
ColumnValueSegmentPrunerTest#testParallelPruningSelectsTheSameSegmentsbuilds 40 segments — half matching the predicate, half prunable — and asserts the parallel path selects exactly the same set as the serial one. 52 pruner tests pass; spotless, checkstyle and license clean.Stack
Part 12, based on #19511.
🤖 Generated with Claude Code