Skip to content

feat: Extend FilterSegmentPruner to prune on ClusterGroupTuples - #20149

Open
cecemei wants to merge 5 commits into
apache:masterfrom
cecemei:shard2
Open

feat: Extend FilterSegmentPruner to prune on ClusterGroupTuples #20149
cecemei wants to merge 5 commits into
apache:masterfrom
cecemei:shard2

Conversation

@cecemei

@cecemei cecemei commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Description

FilterSegmentPruner currently only prunes segments using shard-spec domain ranges (ShardSpec#possibleInDomain). This adds a second, independent check against DataSegment#getClusterGroups(). When a segment carries ClusterGroupTuples, the pruner now also excludes the segment if none of its tuples can satisfy the filter.


This PR has:

  • been self-reviewed.
  • added documentation for new or modified features or behaviors.
  • a release note entry in the PR description.
  • added Javadocs for most classes and all non-trivial methods. Linked related entities via Javadoc links.
  • added or updated version, license, or notice information in licenses.yaml
  • added comments explaining the "why" and the intent of the code wherever would not be obvious for an unfamiliar reader.
  • added unit tests or modified existing tests to cover new code paths, ensuring the threshold for code coverage is met.
  • added integration tests.
  • been tested in a test Druid cluster.

@cecemei cecemei changed the title pruning feat: Extend FilterSegmentPruner to prune on ClusterGroupTuples Aug 25, 2026
@cecemei
cecemei marked this pull request as ready for review August 25, 2026 19:14
final VirtualColumns.Node domainNode = domainVirtualColumns.getNode(column);
if (domainNode != null) {
final VirtualColumn queryEquivalent = getQueryEquivalent(domainNode);
if (queryEquivalent != null && filterFields.contains(queryEquivalent.getOutputName())) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that cluster group virtual columns end up being directly queryable. If this is right then the matching should apply to both the virtual column and to the materialized column (i.e. WHERE group_key = 'something', where group_key is the name of the materialized cluster column.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean that we should match virtual column for both and never match a virtual column (either from clustering group or query) with a physical column? I just added that check, also with test testPruneClusterGroupTuplesVirtualColumnNoQueryVirtualColumnNeverPrunes and testPruneClusterGroupTuplesShadowedByQueryVirtualColumnNeverPrunes

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant that when a cluster group tuple has a part defined by a virtual column, we should be able to prune on that part if it's referenced either using a query-time virtual column, or referenced directly. To me it looks like the current code only works if it's referenced through a query-time virtual column.

The scenario I'm talking about is like:

  • cluster group key is (vdim1) defined as concat(dim1, 'foo')
  • we have a segment using that cluster group config, with cluster groups ('abcfoo') and ('deffoo')
  • query WHERE CONCAT(dim1, 'foo') = 'barfoo' should prune out this segment
  • query WHERE vdim1 = 'barfoo' should also prune out this segment


// same expression, same name
VirtualColumns queryVirtualColumns = VirtualColumns.create(
new ExpressionVirtualColumn("vdim1", "concat(dim1, 'foo')", ColumnType.STRING, TestExprMacroTable.INSTANCE)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a test that confirms vdim1 set to a different expression never prunes.

Please also add a test for querying vdim1 directly when there is no virtual column with that name. (It should prune when cluster groups are present on the segment, because in that case vdim1 is materialized.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have testPruneClusterGroupTuples for testing querying directly.

@FrankChen021 FrankChen021 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Severity Findings
P0 0
P1 1
P2 0
P3 0
Total 1
Severity Findings
P0 0
P1 1
P2 0
P3 0
Total 1

Reviewed 2 of 2 changed files.

Validation: focused git diff --check passed. Builds and tests were not run.


This is an automated review by Codex GPT-5.6-Luna(max)

);
optFilterRangeSet.ifPresent(rangeSet -> filterDomain.put(column, rangeSet));
}
} else if (filterFields.contains(column)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Ignores query-VC shadowing of physical cluster columns

When a query virtual column has the same name as a physical clustering column but a different expression, query VCs shadow physical columns. This branch still applies the filter range to the physical tuple value, so a segment can be pruned even though the virtual expression matches rows inside it. Resolve query virtual columns first and only use direct tuple matching when no query VC shadows the name; add a regression test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated to make sure never prunes when query VC shadows physical column

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed the prior same-name physical-column shadowing finding is resolved by the current guard and regression test. A separate P1 remains at processing/src/main/java/org/apache/druid/query/filter/FilterSegmentPruner.java:220: query virtual-column dependencies can shadow physical inputs, causing incorrect cluster-group pruning. Reviewed 2 of 2 changed files.

@FrankChen021 FrankChen021 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Severity Findings
P0 0
P1 1
P2 0
P3 0
Total 1

Reviewed 2 of 2 changed files. The prior same-name physical-column shadowing concern is resolved by the current guard and regression test; this review found a separate dependency-shadowing correctness issue.

Validation: focused git diff --check passed. Builds and tests were not run.


This is an automated review by Codex GPT-5.6-Luna(max)

{
final VirtualColumns.Node domainNode = domainVirtualColumns.getNode(column);
if (domainNode != null) {
final VirtualColumn queryEquivalent = getQueryEquivalent(domainNode);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Avoid shadowed query dependencies in VC equivalence

getQueryEquivalent(domainNode) matches a segment-side virtual column without accounting for query virtual-column dependencies. For example, with segment v0 = dim1 and query VCs dim1 = dim2 plus q = dim1, q can be treated as equivalent to v0 even though it reads dim2. The pruner can then use a range for q against the wrong cluster-group value and prune segments that contain matching rows. Resolve equivalence from the query dependency graph, and add a regression test for this shadowed-dependency case.

@FrankChen021 FrankChen021 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Severity Findings
P0 0
P1 1
P2 0
P3 0
Total 1

The new virtual-column dependency accounting can conflate a query virtual column with a physical column that shadows one of its dependencies. For example, with physical a=x and v0=concat(a,b), and query virtual columns b=x and q=concat(b,b), the rewritten expressions can look identical even though query b shadows physical b. This can cause cluster-group pruning to discard segments that still contain matching rows. Compare resolved bindings or reject these rewrite collisions, and add a nested-shadow regression test.

Reviewed 3 of 3 changed files.

Validation: focused git diff --no-ext-diff --check passed. Builds and tests were not run.


This is an automated review by Codex GPT-5.6-Luna(max)

if (matched != null &&
// guardrail check for expression collision when a virtual column shadows the physical column
// e.x. otherNode v0 = dim1 and VCs dim1 = dim2 plus q = dim1, q can be treated as equivalent to v0 even though it reads physical column dim2
getNode(matched.getOutputName()).getDependencies().size() == otherNode.getDependencies().size()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Virtual-column dependency counting misses physical-name collisions

The dependency count can conflate a query virtual column with a physical column that shadows one of its dependencies. For example, with physical a=x and v0=concat(a,b), and query virtual columns b=x and q=concat(b,b), the rewritten expressions can look identical even though query b shadows physical b. The cluster-group pruner can then treat q as v0 and discard segments that contain matching rows. Compare resolved bindings or reject rewrite collisions, and add a nested-shadow regression test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants