[#11706] fix(catalog-hive): replace switch-on-enum with if-else in HiveCatalogCapability.caseSensitiveOnName()#11707
Conversation
…Capability.caseSensitiveOnName() SchemaNormalizeDispatcher calls caseSensitiveOnName() outside the IsolatedClassLoader.withClassLoader() boundary. The switch-on-enum causes the Java compiler to generate a synthetic $1 helper class. In certain classloader initialization timing windows the server classloader ends up requesting $1 and cannot find it; the JVM then permanently caches this load failure for the process lifetime, so all subsequent calls throw NoClassDefFoundError until restart. Replace switch with if-else using == on enum constants. This compiles to if_acmpeq (reference comparison) with no synthetic class, making caseSensitiveOnName() safe to call from any classloader context. Add TestHiveCatalogCapability covering: - case-insensitive scopes (SCHEMA, TABLE, COLUMN) return unsupported - case-sensitive scopes (FILESET, TOPIC, PARTITION, MODEL) return supported - HiveCatalogCapability$1 does not exist in the compiled output
There was a problem hiding this comment.
Pull request overview
This PR addresses an intermittent NoClassDefFoundError caused by the compiler-generated synthetic $1 helper class from a switch-on-enum in HiveCatalogCapability.caseSensitiveOnName(), which can be invoked outside the catalog’s isolated classloader context.
Changes:
- Replaced
switch-on-enum withif/elseenum reference comparisons inHiveCatalogCapability.caseSensitiveOnName()to avoid generatingHiveCatalogCapability$1. - Added unit tests validating case sensitivity behavior for different
Capability.Scopevalues. - Added a regression test asserting
HiveCatalogCapability$1is absent from compiled output.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
catalogs/catalog-hive/src/main/java/org/apache/gravitino/catalog/hive/HiveCatalogCapability.java |
Removes enum-switch bytecode pattern to prevent synthetic $1 generation and classloader-related failures. |
catalogs/catalog-hive/src/test/java/org/apache/gravitino/catalog/hive/TestHiveCatalogCapability.java |
Adds behavior tests plus a guard test ensuring the synthetic $1 class is not generated. |
Code Coverage Report
Files
|
…ean test signature - Add explicit null check in caseSensitiveOnName() to throw NPE for null scope, preserving the same fail-fast behaviour the original switch-on-enum had (switch throws NPE on null by JLS spec). - Remove redundant 'throws ClassNotFoundException' from testCaseSensitiveOnNameSyntheticClassAbsent(): the exception is fully handled inside assertThrows and cannot escape the test method.
diqiu50
left a comment
There was a problem hiding this comment.
Thanks for your contribution The fix is correct, but it's a workaround rather than a root fix. The real issue is that CatalogWrapper.capabilities() returns a catalog-classloader-loaded object that is then used outside withClassLoader() — any method on a Capability implementation that touches plugin-jar classes will hit the same problem. Worth a follow-up issue to ensure all Capability method calls are made within the correct classloader context.
|
If this problem did exist for the Hive catalog, we may need to check all catalogs to avoid similar problem. |
Replace manual 'throw new NullPointerException' with Preconditions.checkArgument(scope != null, ...) to follow the Gravitino coding convention. Also add a dedicated test testCaseSensitiveOnNameNullScopeThrows to verify the IllegalArgumentException is thrown for null scope.
Thanks for the note! That's a valid concern — any catalog Capability implementation that references plugin-jar classes could hit the same issue. I've opened #11726 to track a proper root fix. Looking forward to your investigation, and happy to help if needed. |
|
Hi @diqiu50 @yuqi1129, thanks for the valuable feedback! Two updates:
CI is now passing. Could this PR be merged as-is, with the root fix tracked in #11726? |
|
I have no more comments. |
I don’t think this workaround should be merged. We should fix the real problem instead. |
Thanks for the clear direction, @diqiu50! I've implemented the root fix in a new PR: #11755 The approach wraps the This makes the |
|
@JoegenUSTC |
The issue was observed with JDK 17 (the version required by Gravitino's build). |
More details are preferable, and we have never run into the problem in JDK17(JDK17 is also our debugging JDK version). Can you paste the result of |
|
@diqiu50 @yuqi1129 — Thanks again for the valuable feedback on both PRs. I wanted to share a new development and get your thoughts. Following @diqiu50's direction to address the root problem, I opened #11755 with a JDK proxy approach. During review, @yuqi1129 raised a concern — supported by a small standalone test — that the proxy's This suggests the classloader lifecycle fix (ensuring Given this, I'd like to gently revisit whether this PR could be merged as a well-scoped, targeted fix for the immediate problem. I fully respect @diqiu50's preference to avoid landing workarounds, and I remain committed to the lifecycle root fix in #11726. That said, since the root fix will take more time to design and validate properly, would you both be open to landing this as a stable interim improvement while #11726 progresses? Happy to hear your thoughts — and thank you both for the continued engagement. |
What changes were proposed in this pull request?
Replace the
switch-on-enum withif-elseinHiveCatalogCapability.caseSensitiveOnName(), and add unit tests toverify both the functional behavior and the absence of the synthetic
$1class in the compiled output.Why are the changes needed?
Fix: #11706
SchemaNormalizeDispatchercallscaseSensitiveOnName()afterIsolatedClassLoader.withClassLoader()exits — i.e. outside the catalog'sisolated classloader context. The
switch-on-enum causes the Java compilerto generate a synthetic helper class
HiveCatalogCapability$1. In certainclassloader initialization timing windows, the server classloader is involved
in loading
$1but cannot find it in the server classpath. The JVMpermanently caches this load failure for the process lifetime, causing all
subsequent calls to throw
NoClassDefFoundErroruntil restart.Replacing
switchwithif-elseusing==on enum constants compiles toif_acmpeq(reference comparison) with no synthetic class dependency, makingcaseSensitiveOnName()safe to call from any classloader context.Does this PR introduce any user-facing change?
No. The method behavior is identical; only the compiled representation changes.
How was this patch tested?
Added
TestHiveCatalogCapabilitywith three test methods:SCHEMA,TABLE,COLUMN) returnunsupportedFILESET,TOPIC,PARTITION,MODEL) returnsupportedHiveCatalogCapability$1does not exist in the compiled output(guards against future regression back to
switch)