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

The big ClassLoader change #6411

Merged
merged 1 commit into from
Jan 22, 2020
Merged

Conversation

stuartwdouglas
Copy link
Member

@stuartwdouglas stuartwdouglas commented Jan 6, 2020

Fixes #5597 #1724 #5354 #770 #3917 #6652

This changes the way Quarkus ClassLoading works,
to allow for isolated class loaders.

It also unifies how Quarkus is launched, so every
different mode we support uses the same mechanism
for both curation and launch.

Tests are now run in an isolated ClassLoader, which
means that a proxy is created that runs the tests
from within the isolated ClassLoader. This currently
has a quirk where @BeforeAll methods are run twice,
which will be fixed in the next JUnit release. This
can be worked around by using @QuarkusBeforeAll.

Apparently JUnit 5.6 which will fix this should be released on the 20th according to https://github.com/junit-team/junit5/milestone/46, which will allow this restriction to be removed.

This still needs performance testing, but otherwise is very close to being complete.

All of these should be addressed in the new few days.

@gastaldi gastaldi marked this pull request as ready for review January 6, 2020 12:52
@stuartwdouglas stuartwdouglas force-pushed the new-class-loading2 branch 4 times, most recently from 3e02fa7 to 48c6c20 Compare January 7, 2020 03:19
Copy link
Member

@aloubyansky aloubyansky left a comment

Choose a reason for hiding this comment

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

I haven't yet reviewed it completely. I'll probably do it in a few steps as it's quite large and not trivial.

@stuartwdouglas stuartwdouglas force-pushed the new-class-loading2 branch 2 times, most recently from 5cab6bb to 956aa03 Compare January 8, 2020 02:00
@stuartwdouglas
Copy link
Member Author

This commit includes a class loading guide that gives a bit of an explanation as to how it all works, when reviewing this is probably a good place to start.

gsmet
gsmet previously requested changes Jan 8, 2020
Copy link
Member

@gsmet gsmet left a comment

Choose a reason for hiding this comment

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

I will dig more into this but here is a small review of the documentation part.

docs/src/main/asciidoc/class-loading-reference.adoc Outdated Show resolved Hide resolved
This loads all the `-deployment` artifacts and their dependencies, as well as other user dependencies. It does not load the
application root or any hot deployed code. This ClassLoader is persistent, even if the application restarts it will remain
(which is why it cannot load application classes that may be hot deployed). It's parent is the base ClassLoader, and it is
transformer safe.
Copy link
Member

Choose a reason for hiding this comment

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

I think we should have an explanation of what that means.

Copy link
Contributor

Choose a reason for hiding this comment

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

The explanation exists, but it's further down, so I agree it should be moved up here

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you also please add an explanation why this classloader needs to be transformer safe? Is this need driven by the fact that we need to transform dependencies in some cases?

docs/src/main/asciidoc/class-loading-reference.adoc Outdated Show resolved Hide resolved

This ClassLoader is non-persistent, it will be re-created when the application is started, and is isolated. This ClassLoader
is the context ClassLoader that is used when running the build steps. It is transformer safe so loading application classes
will not prevent them from being transformed.
Copy link
Member

Choose a reason for hiding this comment

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

Oh, you explain it there.

docs/src/main/asciidoc/class-loading-reference.adoc Outdated Show resolved Hide resolved
docs/src/main/asciidoc/class-loading-reference.adoc Outdated Show resolved Hide resolved
docs/src/main/asciidoc/class-loading-reference.adoc Outdated Show resolved Hide resolved
docs/src/main/asciidoc/class-loading-reference.adoc Outdated Show resolved Hide resolved
There are some dependencies that we can be sure we do not want. This generally happens when a dependency has had a name
change (e.g. smallrye-config changing groups from `org.smallrye` to `org.smallrye.config`, the `javax` -> `jakarta` rename).
This can cause problems, as if these artifacts end up in the dependency tree out of date classes can be loaded that are
not compatible with Quarkus. To deal with this this extensions can specify artifacts that should never be loaded. This is
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
not compatible with Quarkus. To deal with this this extensions can specify artifacts that should never be loaded. This is
not compatible with Quarkus. To deal with this, extensions can specify artifacts that should never be loaded. This is

docs/src/main/asciidoc/class-loading-reference.adoc Outdated Show resolved Hide resolved

Base Runtime ClassLoader::

This loads all the runtime extension dependencies, as well as other user dependencies. It does not load the
Copy link
Contributor

Choose a reason for hiding this comment

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

as well as other user dependencies is mentioned here for Base Runtime Classloader, however Augment ClassLoader also mentions as well as other user dependencies.

This seems kind of confusing, but I assume that the former is done at build time and the latter at runtime? Maybe a note should be made about this?


This loads code that is not hot-reloadable, but it does support transformation (although once the class is loaded this
transformation is no longer possible). This means that only transformers registered in the first application start
will take effect, however as these transformers are expected to be idempotent this should not cause problems.
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we have an example of what sort of transformations are meant here?

@geoand
Copy link
Contributor

geoand commented Jan 8, 2020

I took at this a little and I really love the way things get a lot cleaner

@stuartwdouglas stuartwdouglas force-pushed the new-class-loading2 branch 7 times, most recently from 05ca120 to 47ae5f5 Compare January 10, 2020 01:11
@stuartwdouglas
Copy link
Member Author

The windows tests are failing because something somewhere is not closing a file handle for the libs/$app}.jar file in the gradle build. The gradle tests can't delete their temp folders and fail as a result.

@stuartwdouglas stuartwdouglas force-pushed the new-class-loading2 branch 2 times, most recently from 6ab8421 to 92f80fe Compare January 13, 2020 09:17
@boring-cyborg boring-cyborg bot added area/core area/dependencies Pull requests that update a dependency file labels Jan 20, 2020
@stuartwdouglas stuartwdouglas force-pushed the new-class-loading2 branch 2 times, most recently from c17a597 to cab3589 Compare January 21, 2020 00:05
@stuartwdouglas
Copy link
Member Author

@aloubyansky I believe I have fixed all the issues you brought up.

@gsmet I have fixed the docs issues. I have tested against the platform test suite, and there are some issues that will likely need to be fixed on the camel side (e.g. the XML parsing issue I mention above). Once this is in I will work with them to get this resolved.

Files.createDirectories(configFile.getParent());
Files.write(configFile, sb.toString().getBytes(StandardCharsets.UTF_8),
Files.exists(configFile) ? new OpenOption[] { StandardOpenOption.APPEND } : new OpenOption[] {});
Files.deleteIfExists(temp);
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't it be deleted in the finally?

classWriter.write(i.getClassData());
}
log.infof("Wrote %s", classFile.getAbsolutePath());
} catch (Throwable t) {
Copy link
Member

Choose a reason for hiding this comment

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

Does it really have to be a Throwable? Swallowing throwables in a loop may go pretty bad.

@stuartwdouglas stuartwdouglas force-pushed the new-class-loading2 branch 3 times, most recently from 1553a1d to ee88497 Compare January 21, 2020 11:58
Copy link
Member

@aloubyansky aloubyansky left a comment

Choose a reason for hiding this comment

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

Let's get this in, ASAP!

@stuartwdouglas stuartwdouglas force-pushed the new-class-loading2 branch 4 times, most recently from 751414c to a0d6e82 Compare January 22, 2020 07:40
@geoand
Copy link
Contributor

geoand commented Jan 22, 2020

Damn CI is acting up again...

@stuartwdouglas
Copy link
Member Author

I made another modification today as I found a class loader leak, if this passed it should be good to merge

@stuartwdouglas
Copy link
Member Author

CI seems to be working again, @gsmet if it passes can you hit the button?

@gsmet gsmet added the triage/waiting-for-ci Ready to merge when CI successfully finishes label Jan 22, 2020
@gsmet
Copy link
Member

gsmet commented Jan 22, 2020

Sure.

@gsmet gsmet dismissed their stale review January 22, 2020 11:15

Comments addressed.

This changes the way Quarkus ClassLoading works,
to allow for isolated class loaders.

It also unifies how Quarkus is launched, so every
different mode we support uses the same mechanism
for both curation and launch.

Tests are now run in an isolated ClassLoader, which
means that a proxy is created that runs the tests
from within the isolated ClassLoader. This currently
has a quirk where @BeforeAll methods are run twice,
which will be fixed in the next JUnit release. This
can be worked around by using @QuarkusBeforeAll.
@gsmet gsmet force-pushed the new-class-loading2 branch from 9b73548 to b67491c Compare January 22, 2020 12:30
@gsmet
Copy link
Member

gsmet commented Jan 22, 2020

I rebased to trigger a new CI run... This CI issue is a nightmare :/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/config area/core area/dependencies Pull requests that update a dependency file release/noteworthy-feature triage/waiting-for-ci Ready to merge when CI successfully finishes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Classloader Isolation Model
6 participants