Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Plexus ClassWorld for more control over the classloaders #13845

Closed
wants to merge 18 commits into from

Conversation

rfscholte
Copy link
Contributor

@rfscholte rfscholte commented Aug 19, 2024

There is a known and longstanding issue with pinot plugins and class colissions. The problem lies in not being able to control where classes should be coming from. In the end it is all about classloaders.
The JRE recognized this issue and came with the Java Platform Module System, but that comes with other requirements.
Apache Maven also has the concept of plugins and has been using Plexus Classworld to solve that issue (see https://maven.apache.org/guides/mini/guide-maven-classloading.html)
With Plexus Classworld it is possible to define the order of classloaders, scope the exported and imported packages, etc. , see https://codehaus-plexus.github.io/plexus-classworlds/ .
This PR will introduce a library that has proven itself. Even if discover other classloading issues, it will possible to configure Plexus Classworld to solve that.

@codecov-commenter
Copy link

codecov-commenter commented Aug 19, 2024

Codecov Report

Attention: Patch coverage is 29.41176% with 48 lines in your changes missing coverage. Please review.

Project coverage is 57.56%. Comparing base (59551e4) to head (440db9d).
Report is 931 commits behind head on master.

Files Patch % Lines
...ava/org/apache/pinot/spi/plugin/PluginManager.java 35.71% 31 Missing and 5 partials ⚠️
...che/pinot/spi/plugin/PinotPluginConfiguration.java 0.00% 12 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master   #13845      +/-   ##
============================================
- Coverage     61.75%   57.56%   -4.19%     
+ Complexity      207      197      -10     
============================================
  Files          2436     2578     +142     
  Lines        133233   142305    +9072     
  Branches      20636    22102    +1466     
============================================
- Hits          82274    81914     -360     
- Misses        44911    53899    +8988     
- Partials       6048     6492     +444     
Flag Coverage Δ
custom-integration1 <0.01% <0.00%> (-0.01%) ⬇️
integration <0.01% <0.00%> (-0.01%) ⬇️
integration1 <0.01% <0.00%> (-0.01%) ⬇️
integration2 0.00% <0.00%> (ø)
java-11 57.51% <29.41%> (-4.20%) ⬇️
java-21 57.44% <29.41%> (-4.18%) ⬇️
skip-bytebuffers-false 57.53% <29.41%> (-4.21%) ⬇️
skip-bytebuffers-true 57.42% <29.41%> (+29.69%) ⬆️
temurin 57.56% <29.41%> (-4.19%) ⬇️
unittests 57.55% <29.41%> (-4.19%) ⬇️
unittests1 40.27% <29.41%> (-6.62%) ⬇️
unittests2 27.84% <0.00%> (+0.11%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

pinot-spi/pom.xml Outdated Show resolved Hide resolved
Copy link
Contributor

@Jackie-Jiang Jackie-Jiang left a comment

Choose a reason for hiding this comment

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

Are jar files included intentionally? It will add almost 40MB to the repo

@Jackie-Jiang Jackie-Jiang added enhancement dependencies Pull requests that update a dependency file labels Aug 19, 2024
@rfscholte
Copy link
Contributor Author

Are jar files included intentionally? It will add almost 40MB to the repo

It can be replaced with any jar, but that means it will be a customized jar. What I can do is remove all the unused (untested) classes from this jar. Or do you have something else in mind?

@gortiz
Copy link
Contributor

gortiz commented Aug 20, 2024

Question about the Jars: If we change the test to be an integration test and we use failsafe instead of surfire... would we be able to consume the actual jar from the plugins instead of the ones we include in resources?

@rfscholte
Copy link
Contributor Author

I guess the question is: can we let Maven download the jars instead? In this case the jars are test-resources and not dependencies, so I wouldn't abuse test-scoped dependencies. What we can do is to make use of https://maven.apache.org/plugins/maven-dependency-plugin/copy-mojo.html to copy the dependencies to the preferred location. Let me prepare that. (in this case it is still a unittest, so no benefits when using failsafe)

ClassLoader pinotLoader = _classWorld.getClassRealm("pinot");

// All exported packages
Stream.of("org.apache.pinot.spi.accounting",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This list is an assumption. Plugins should only make use of classes in these packages, which is any inside the pinot-spi module. Any other pinot-packages will not be accessible.

@@ -41,11 +47,16 @@ public class PluginManager {

public static final String PLUGINS_DIR_PROPERTY_NAME = "plugins.dir";
public static final String PLUGINS_INCLUDE_PROPERTY_NAME = "plugins.include";
public static final String PLUGINS_LOADER_LEGACY_PROPERTY_NAME = "pinot.plugins.loader.legacy";
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've introduced a system property to fall back to the original behavior. I can imagine that some plugins rely on internal pinot-classes, which will now fail.

@Jackie-Jiang
Copy link
Contributor

Could you please update the PR description with some context to help people understand the intention of the PR?

Comment on lines 44 to 49
<execution>
<id>copy-pinot-plugins</id>
<phase>process-test-resources</phase>
<goals>
<goal>copy</goal>
</goals>
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: I think this is a good idea but probably devs are not going to understand on a first read why this plugin is necessary.

Can you add a comment here saying that PluginManagerTest requires these resources and we decided to copy them at build time?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed, this could use an extra comment

@gortiz
Copy link
Contributor

gortiz commented Aug 21, 2024

PluginManagerTest test is failing right now with:

java.lang.RuntimeException: org.codehaus.plexus.classworlds.realm.NoSuchRealmException: pinot-dropwizard
	at org.apache.pinot.spi.plugin.PluginManager.loadClass(PluginManager.java:380)
	at org.apache.pinot.spi.plugin.PluginManagerTest.classRealms(PluginManagerTest.java:217)

@@ -41,10 +51,18 @@ public class PluginManager {

public static final String PLUGINS_DIR_PROPERTY_NAME = "plugins.dir";
public static final String PLUGINS_INCLUDE_PROPERTY_NAME = "plugins.include";
public static final String PLUGINS_LOADER_LEGACY_PROPERTY_NAME = "pinot.plugins.loader.legacy";
public static final String PLUGINS_LOADER_LEGACY_DEFAULT = "false";
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be true for now.

Copy link
Contributor

Choose a reason for hiding this comment

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

Probably we should rename the property to be something like pinot.plugins.loader=plexus. In case the property is not defined, we use the legacy mode. In the future we will be able to change the default to plexus.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we make legacy the default, I wonder if anyone dares to switch to plexus classworld, even though classloader issues keep happening. Instead I propose to make plexus classworlds behave more like its current behavior. i.e. having access to all classes (like now) but loading them in the expected order. In other words: make them unlimited. The switch to limited as default is smaller.

Copy link
Contributor

Choose a reason for hiding this comment

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

We are used to use flags to enable new features to be sure we do not affect deployments where the flag is not set. I'm sure StarTree will use the new system in their deployments (after testing it for a while), so I'm sure it is going to be used.

@rfscholte rfscholte mentioned this pull request Aug 29, 2024
@gortiz
Copy link
Contributor

gortiz commented Sep 3, 2024

I'm going to close this PR because Robert has been focused on #13907, which also includes the new plugin layout and simplifies the logic here

@gortiz gortiz closed this Sep 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependencies Pull requests that update a dependency file enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants