Skip to content

[SPARK-42539][SQL][HIVE] Eliminate separate classloader when using 'builtin' Hive version for metadata client#40144

Closed
xkrogen wants to merge 5 commits into
apache:masterfrom
xkrogen:xkrogen/SPARK-42539/hive-isolatedclientloader-builtin-user-jar-conflict-fix/java9strategy
Closed

[SPARK-42539][SQL][HIVE] Eliminate separate classloader when using 'builtin' Hive version for metadata client#40144
xkrogen wants to merge 5 commits into
apache:masterfrom
xkrogen:xkrogen/SPARK-42539/hive-isolatedclientloader-builtin-user-jar-conflict-fix/java9strategy

Conversation

@xkrogen

@xkrogen xkrogen commented Feb 23, 2023

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

When using the 'builtin' Hive version for the Hive metadata client, do not create a separate classloader, and rather continue to use the overall user/application classloader (regardless of Java version). This standardizes the behavior for all Java versions with that of Java 9+. See SPARK-42539 for more details on why this approach was chosen.

Why are the changes needed?

Please see a much more detailed description in SPARK-42539. The tl;dr is that user-provided JARs (such as hive-exec-2.3.8.jar) take precedence over Spark/system JARs when constructing the classloader used by IsolatedClientLoader on Java 8 in 'builtin' mode, which can cause unexpected behavior and/or breakages. This violates the expectation that, unless user-first classloader mode is used, Spark JARs should be prioritized over user JARs. It also seems that this separate classloader was unnecessary from the start, since the intent of 'builtin' mode is to use the JARs already existing on the regular classloader (as alluded to here). The isolated clientloader was originally added in #5876 in 2015. This bit in the PR description is the only mention of the behavior for "builtin":

attempt to discover the jars that were used to load Spark SQL and use those. This option is only valid when using the execution version of Hive.

I can't follow the logic here; the user classloader clearly has all of the necessary Hive JARs, since that's where we're getting the JAR URLs from, so we could just use that directly instead of grabbing the URLs. When this was initially added, it only used the JARs from the user classloader, not any of its parents, which I suspect was the motivating factor (to try to avoid more Spark classes being duplicated inside of the isolated classloader, I guess). But that was changed a month later anyway in #6435 / #6459, so I think this may have basically been deadcode from the start. It has also caused at least one issue over the years, e.g. SPARK-21428, which disables the new-classloader behavior in the case of running inside of a CLI session.

Does this PR introduce any user-facing change?

No, except to protect Spark itself from potentially being broken by bad user JARs.

How was this patch tested?

This includes a new unit test in HiveUtilsSuite which demonstrates the issue and shows that this approach resolves it. It has also been tested on a live cluster running Java 8 and Hive communication functionality continues to work as expected.

…e general spark user classloader when 'builtin' is used instead of reconstructing a new URL classloader
@xkrogen

xkrogen commented Feb 23, 2023

Copy link
Copy Markdown
Contributor Author

cc @sunchao @AngersZhuuuu since you've worked on somewhat-related changes in #34690, #32887, etc.
cc @srowen @squito since you were involved in #24057 for the Java 9+ changes
cc @HyukjinKwon @dongjoon-hyun for any general interest

@dongjoon-hyun

Copy link
Copy Markdown
Member

Also, cc @mridulm , @cloud-fan , @rednaxelafx, @zsxwing, @kiszk , @maropu .

@cloud-fan

cloud-fan commented Feb 24, 2023

Copy link
Copy Markdown
Contributor

It makes sense to use the builtin classloader when using builtin Hive. To clarify: we still have the class loading issue if people specifies a certain hive version (not builtin), right?

@xkrogen

xkrogen commented Feb 24, 2023

Copy link
Copy Markdown
Contributor Author

Great question @cloud-fan , and actually no, we don't. For all of the other values of spark.sql.hive.metastore.jars besides 'builtin', the user JARs are not included at all (refer to this section of HiveUtils). In all of those cases, the JAR list is constructed purely from the dependencies for the specified Hive version. Whether that behavior is correct is another question -- @shardulm94 informed me that user JARs are required to support custom serdes inside of the Hive client -- but in any case, 'builtin' is the only mode that is susceptible to this issue.

@xkrogen xkrogen changed the title [SPARK-42539][SQL][HIVE] Elminiate separate classloader when using 'builtin' Hive version for metadata client [SPARK-42539][SQL][HIVE] Eliminate separate classloader when using 'builtin' Hive version for metadata client Feb 24, 2023
@cloud-fan

Copy link
Copy Markdown
Contributor

lgtm if all tests pass

@sunchao sunchao 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.

LGTM too

Comment thread sql/hive/src/test/scala/org/apache/spark/sql/hive/HiveUtilsSuite.scala Outdated
Comment thread sql/hive/src/test/scala/org/apache/spark/sql/hive/HiveUtilsSuite.scala Outdated
@sunchao sunchao closed this in 27ad583 Feb 27, 2023
sunchao pushed a commit that referenced this pull request Feb 27, 2023
…uiltin' Hive version for metadata client

### What changes were proposed in this pull request?
When using the 'builtin' Hive version for the Hive metadata client, do not create a separate classloader, and rather continue to use the overall user/application classloader (regardless of Java version). This standardizes the behavior for all Java versions with that of Java 9+. See SPARK-42539 for more details on why this approach was chosen.

### Why are the changes needed?
Please see a much more detailed description in SPARK-42539. The tl;dr is that user-provided JARs (such as `hive-exec-2.3.8.jar`) take precedence over Spark/system JARs when constructing the classloader used by `IsolatedClientLoader` on Java 8 in 'builtin' mode, which can cause unexpected behavior and/or breakages. This violates the expectation that, unless user-first classloader mode is used, Spark JARs should be prioritized over user JARs. It also seems that this separate classloader was unnecessary from the start, since the intent of 'builtin' mode is to use the JARs already existing on the regular classloader (as alluded to [here](#24057 (comment))). The isolated clientloader was originally added in #5876 in 2015. This bit in the PR description is the only mention of the behavior for "builtin":
> attempt to discover the jars that were used to load Spark SQL and use those. This option is only valid when using the execution version of Hive.

I can't follow the logic here; the user classloader clearly has all of the necessary Hive JARs, since that's where we're getting the JAR URLs from, so we could just use that directly instead of grabbing the URLs. When this was initially added, it only used the JARs from the user classloader, not any of its parents, which I suspect was the motivating factor (to try to avoid more Spark classes being duplicated inside of the isolated classloader, I guess). But that was changed a month later anyway in #6435 / #6459, so I think this may have basically been deadcode from the start. It has also caused at least one issue over the years, e.g. SPARK-21428, which disables the new-classloader behavior in the case of running inside of a CLI session.

### Does this PR introduce _any_ user-facing change?
No, except to protect Spark itself from potentially being broken by bad user JARs.

### How was this patch tested?
This includes a new unit test in `HiveUtilsSuite` which demonstrates the issue and shows that this approach resolves it. It has also been tested on a live cluster running Java 8 and Hive communication functionality continues to work as expected.

Closes #40144 from xkrogen/xkrogen/SPARK-42539/hive-isolatedclientloader-builtin-user-jar-conflict-fix/java9strategy.

Authored-by: Erik Krogen <xkrogen@apache.org>
Signed-off-by: Chao Sun <sunchao@apple.com>
@sunchao

sunchao commented Feb 27, 2023

Copy link
Copy Markdown
Member

Merged to master/branch-3.4. Thanks @xkrogen !

@xkrogen xkrogen deleted the xkrogen/SPARK-42539/hive-isolatedclientloader-builtin-user-jar-conflict-fix/java9strategy branch February 27, 2023 23:04
@xkrogen

xkrogen commented Feb 27, 2023

Copy link
Copy Markdown
Contributor Author

Thanks @sunchao and @cloud-fan !

@HyukjinKwon

Copy link
Copy Markdown
Member

Seems like the tests didn't pass .. I am reverting this as it causes a lot of test failures. e.g.)

@dongjoon-hyun

Copy link
Copy Markdown
Member

Thank you for recovering master branch by reverting, @HyukjinKwon ! The reverting unblocks other PRs.

@cloud-fan

Copy link
Copy Markdown
Contributor

Shall we revert it from 3.4 as well?

@dongjoon-hyun

dongjoon-hyun commented Feb 28, 2023

Copy link
Copy Markdown
Member

Yes, it was reverted here, 26009d4.

@sunchao

sunchao commented Feb 28, 2023

Copy link
Copy Markdown
Member

Hmm interesting. Somehow the tests were shown all passing for me when I merged this. Sorry for the trouble.

@xkrogen

xkrogen commented Feb 28, 2023

Copy link
Copy Markdown
Contributor Author

I will take a look at the test failures, thanks @HyukjinKwon for addressing the revert!

@dongjoon-hyun

dongjoon-hyun commented Feb 28, 2023

Copy link
Copy Markdown
Member

If you folks don't mind, shall we consider this for Apache Spark 3.5 only? ClassLoader issue has been tricky always in the community. We need enough time to stabilize in master branch to give a chance to be verified in several cases by different organizations.

@sunchao

sunchao commented Feb 28, 2023

Copy link
Copy Markdown
Member

+1. I agree with @dongjoon-hyun .

@xkrogen

xkrogen commented Feb 28, 2023

Copy link
Copy Markdown
Contributor Author

I agree as well. Posted a new PR at #40224.

sunchao pushed a commit that referenced this pull request Mar 1, 2023
…uiltin' Hive version for metadata client

### What changes were proposed in this pull request?
When using the 'builtin' Hive version for the Hive metadata client, do not create a separate classloader, and rather continue to use the overall user/application classloader (regardless of Java version). This standardizes the behavior for all Java versions with that of Java 9+. See SPARK-42539 for more details on why this approach was chosen.

Please note that this is a re-submit of #40144. That one introduced test failures, and potentially a real issue, because the PR works by setting `isolationOn = false` for `builtin` mode. In addition to adjusting the classloader, `HiveClientImpl` relies on `isolationOn` to determine if it should use an isolated copy of `SessionState`, so the PR inadvertently switched to using a shared `SessionState` object. I think we do want to continue to have the isolated session state even in `builtin` mode, so this adds a new flag `sessionStateIsolationOn` which controls whether the session state should be isolated, _separately_ from the `isolationOn` flag which controls whether the classloader should be isolated. Default behavior is for `sessionStateIsolationOn` to be set equal to `isolationOn`, but for `builtin` mode, we override it to enable session state isolated even though classloader isolation is turned off.

### Why are the changes needed?
Please see a much more detailed description in SPARK-42539. The tl;dr is that user-provided JARs (such as `hive-exec-2.3.8.jar`) take precedence over Spark/system JARs when constructing the classloader used by `IsolatedClientLoader` on Java 8 in 'builtin' mode, which can cause unexpected behavior and/or breakages. This violates the expectation that, unless user-first classloader mode is used, Spark JARs should be prioritized over user JARs. It also seems that this separate classloader was unnecessary from the start, since the intent of 'builtin' mode is to use the JARs already existing on the regular classloader (as alluded to [here](#24057 (comment))). The isolated clientloader was originally added in #5876 in 2015. This bit in the PR description is the only mention of the behavior for "builtin":
> attempt to discover the jars that were used to load Spark SQL and use those. This option is only valid when using the execution version of Hive.

I can't follow the logic here; the user classloader clearly has all of the necessary Hive JARs, since that's where we're getting the JAR URLs from, so we could just use that directly instead of grabbing the URLs. When this was initially added, it only used the JARs from the user classloader, not any of its parents, which I suspect was the motivating factor (to try to avoid more Spark classes being duplicated inside of the isolated classloader, I guess). But that was changed a month later anyway in #6435 / #6459, so I think this may have basically been deadcode from the start. It has also caused at least one issue over the years, e.g. SPARK-21428, which disables the new-classloader behavior in the case of running inside of a CLI session.

### Does this PR introduce _any_ user-facing change?
No, except to protect Spark itself from potentially being broken by bad user JARs.

### How was this patch tested?
This includes a new unit test in `HiveUtilsSuite` which demonstrates the issue and shows that this approach resolves it. It has also been tested on a live cluster running Java 8 and Hive communication functionality continues to work as expected.

Unit tests failing in #40144 have been locally tested (`HiveUtilsSuite`, `HiveSharedStateSuite`, `HiveCliSessionStateSuite`, `JsonHadoopFsRelationSuite`).

Closes #40224 from xkrogen/xkrogen/SPARK-42539/hive-isolatedclientloader-builtin-user-jar-conflict-fix/take2.

Authored-by: Erik Krogen <xkrogen@apache.org>
Signed-off-by: Chao Sun <sunchao@apple.com>
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