Skip to content

HIVE-28480: Disable SMB on partition hash generator mismatch across join branches… - #5406

Merged
kasakrisz merged 4 commits into
apache:masterfrom
himanshu-mishra:HIVE-28480
Sep 9, 2024
Merged

kasakrisz merged 4 commits into
apache:masterfrom
himanshu-mishra:HIVE-28480

Conversation

@himanshu-mishra

Copy link
Copy Markdown
Contributor

… in previous RS

What changes were proposed in this pull request?

As SMB replaces last RS op from the joining branches and the JOIN op with MERGEJOIN, we need to ensure the RS before these RS, in both branches, are partitioning using same hash generator.

Hash code generator differs based on ReducerTraits.UNIFORM i.e. ReduceSinkOperator#computeMurmurHash() or ReduceSinkOperator#computeHashCode(), leading to different hash code for same value.

Skip SMB join in such cases.

Why are the changes needed?

It fixes bug that results in incorrect join result. Example query and behavior explanation is added in https://issues.apache.org/jira/browse/HIVE-28480

Does this PR introduce any user-facing change?

No

Is the change a dependency upgrade?

No

How was this patch tested?

Tested via auto_sortmerge_join_18.q added in this PR

@ngsg

ngsg commented Aug 26, 2024

Copy link
Copy Markdown
Contributor

The patch seems reasonable. LGTM

@okumin okumin left a comment •

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.

This looks fine to me

@@ -0,0 +1,39 @@
CREATE TABLE t_asj_18 (k STRING, v INT);

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 confirmed this test case reproduces the issue explained in the JIRA ticket

Boolean prevRsHasUniformTrait = null;
for (Operator<? extends OperatorDesc> parentOp : joinOp.getParentOperators()) {
// Assertion of mandatory single parent is already being done in bucket version check earlier
Operator<?> op = parentOp.getParentOperators().get(0);

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.

Can I assume we want to find ReduceSinkOperator-1 of Operators -> ReduceSinkOperator-1 -> Operators -> ReduceSinkOperator-2(parentOp) ->JoinOperator?

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.

Yes. The ReduceSinkOperator-2 -> JoinOperator get replaced by MERGEJOIN operator for SMB. We want to ensure the RS before these in both join branches are using same partitioning logic.

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.

That makes sense. Thanks

@himanshu-mishra

Copy link
Copy Markdown
Contributor Author

@okumin if this looks good, please merge the PR

@okumin

okumin commented Aug 28, 2024 •

Copy link
Copy Markdown
Contributor

@himanshu-mishra Sorry, I don't have the right to merge a PR as I am not a committer. You need approval of any committer who is ideally an expert of SMB join.

@himanshu-mishra

Copy link
Copy Markdown
Contributor Author

@kasakrisz can you please review or add reviewer

@kasakrisz kasakrisz left a comment

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.

This is a good candidate to fix this issue, but have you checked why the reducer traits are different?

Comment on lines +883 to +889
while (op != null && !(op instanceof TableScanOperator || op instanceof ReduceSinkOperator
|| op instanceof CommonJoinOperator)) {
// If op has parents it is guaranteed to be 1.
List<Operator<?>> parents = op.getParentOperators();
Preconditions.checkState(parents.size() == 0 || parents.size() == 1);
op = parents.size() == 1 ? parents.get(0) : null;
}

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.

There are several utility methods to find operators in branches of a plan in ql.exec.OperatorUtils

OperatorUtils.find*Operator*()

Is it possible to use one of them to find parent RS operators?

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.

There isn't a utility method there that returns first ancestor operator encountered from given set of classes. Here, we are only interested in ancestor RS if that is the source of data for current RS, if TS, JOIN are encountered, they are the source, and we do not need verifying them.

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.

a few lines earlier the same while loop exists

while (op != null && !(op instanceof TableScanOperator || op instanceof ReduceSinkOperator
|| op instanceof CommonJoinOperator)) {
// If op has parents it is guaranteed to be 1.
List<Operator<?>> parents = op.getParentOperators();
Preconditions.checkState(parents.size() == 0 || parents.size() == 1);
op = parents.size() == 1 ? parents.get(0) : null;
}

Could you please extract these to a utility method and move it to OperatorUtils

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.

Thanks for reviewing. I'll incorporate it.

@himanshu-mishra

Copy link
Copy Markdown
Contributor Author

This is a good candidate to fix this issue, but have you checked why the reducer traits are different?

Consider the sample query

SELECT * FROM (
    SELECT k, COUNT(DISTINCT v), SUM(v)
    FROM t_asj_18 GROUP BY k
) a LEFT JOIN (
    SELECT k, COUNT(v)
    FROM t_asj_18 GROUP BY k
) b ON a.k = b.k; 

In this query the aggregation k, count(distinct v).. group by k leads to group by and hence following RS operator's keys to be k, v while keeping partition column k, as we are aggregating on k. In such cases where key columns and partition columns differ, the UNIFORM trait is removed refer commit.
Note that the group by keys and partition columns differ only with distinct aggregate, removing that would lead to both being a thereby having UNIFORM trait.

@himanshu-mishra

Copy link
Copy Markdown
Contributor Author

@kasakrisz let me know if there are pending concerns.

@kasakrisz

Copy link
Copy Markdown
Contributor

@kasakrisz let me know if there are pending concerns.

Sorry, I was occupied in the last few days. I left only one comment.

@sonarqubecloud

sonarqubecloud Bot commented Sep 5, 2024

Copy link
Copy Markdown

@kasakrisz kasakrisz left a comment

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.

+1

@himanshu-mishra

Copy link
Copy Markdown
Contributor Author

@kasakrisz as you have approved the changes, can you help with merging the PR.

@kasakrisz
kasakrisz merged commit cbfc88e into apache:master Sep 9, 2024
@himanshu-mishra
himanshu-mishra deleted the HIVE-28480 branch September 9, 2024 11:50
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.

5 participants