-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Provide GraalVM metadata and substitutions #1357
Conversation
THe added substitutions are based on https://github.com/quarkusio/quarkus/blob/main/extensions/mongodb-client/runtime/src/main/java/io/quarkus/mongodb/runtime/graal/MongoClientSubstitutions.java JAVA-5219
@Override | ||
public Stream create(final ServerAddress serverAddress) { | ||
return createInternal(serverAddress); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no way to call the original implementation of a method that is being substituted. I had to introduce createInternal
as a work-around.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, something you really want this feature.... I follow the same pattern very (too) often.
|
||
import static com.mongodb.assertions.Assertions.isTrueArgument; | ||
import static com.mongodb.assertions.Assertions.notNull; | ||
|
||
/** | ||
* Represents the location of a MongoD unix domain socket. | ||
* It is {@linkplain SocketStreamFactorySubstitution#create(ServerAddress) not supported in GraalVM native image}. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that because internal classes are excluded from our API docs, this linkplain
produces plain text "not supported in GraalVM native image" in the generated API docs. But in an IDE we still have a working link, which is convenient. The same goes for the change in MongoCompressor
.
Hi, @cescoffier. I am unable to add you as a reviewer of this PR, but your review may be helpful. I am inviting you to take a look at this PR if you have time. |
@Substitute | ||
public Stream create(final ServerAddress serverAddress) { | ||
if (serverAddress instanceof UnixServerAddress) { | ||
throw new UnsupportedOperationException("UnixServerAddress is not supported in GraalVM native image"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming UnixServerAddress
is not working properly in GraalVM native image, this is not a breaking change. But this assumptions is based solely on the substitutions made in Quarkus. The same is true for MongoCompressorSubstitution.createSnappyCompressor
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, it might be possible to support it now. These substitutions are kind of old, like 4/5 years old and GraalVM evolved a lot in between.
Note that the class will need to be marked as a runtime only class (cannot be initialized at build time)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that the class will need to be marked as a runtime only class (cannot be initialized at build time)
Could you please help me understand why? I made the snappy compressor work without any tricks, just by collecting the reachability metadata with the tracing agent via the org.graalvm.buildtools.native
Gradle plugin. And the compression works regardless of whether I specify
--initialize-at-run-time=com.mongodb.internal.connection.SnappyCompressor
- or
--initialize-at-build-time=com.mongodb.internal.connection.SnappyCompressor
P.S. Given that snappy compression works with GraalVM, I removed snappy-related substitutions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The config makes a lot of sense. There are a few differences with what we do in Quarkus.
My main concern is around JNA/JNI, and the cost in term of image size. Maybe it can be made optional.
@Override | ||
public Stream create(final ServerAddress serverAddress) { | ||
return createInternal(serverAddress); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, something you really want this feature.... I follow the same pattern very (too) often.
public final class MongoCompressorSubstitution { | ||
@Substitute | ||
public static MongoCompressor createSnappyCompressor() { | ||
throw new UnsupportedOperationException("Snappy compressor is not supported in GraalVM native image"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not for this PR, but I got Snappy working in native with some nasty work (for kafka and netty). You could reproduce the same kind of thing.
See:
@Substitute | ||
public Stream create(final ServerAddress serverAddress) { | ||
if (serverAddress instanceof UnixServerAddress) { | ||
throw new UnsupportedOperationException("UnixServerAddress is not supported in GraalVM native image"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, it might be possible to support it now. These substitutions are kind of old, like 4/5 years old and GraalVM evolved a lot in between.
Note that the class will need to be marked as a runtime only class (cannot be initialized at build time)
@@ -0,0 +1,22 @@ | |||
[ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do not have these in Quarkus. I need to try with that. It might fix the mongo crypt issues we have in native.
@@ -0,0 +1,96 @@ | |||
[ | |||
{ | |||
"name":"com.mongodb.BasicDBObject", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a slightly different set of reflection registrations. But, I believe yours are more accurate.
driver-core/src/main/resources/META-INF/native-image/resource-config.json
Show resolved
Hide resolved
- Improve logging - Use `clean` instead of `:graalvm-native-image-app:clean` in `readme.md` JAVA-5219
Given that the snappy compressor works in GraalVM, we don't need any substitutions for it. JAVA-5219
Having these entries results in a larger native image, even if the shared libraries are not used by the application. Therefore, we won't supply the entries with our JARs, but will document this for users and propose the `resource-config.json` in :graalvm-native-image-app as an example where users may pick the necessary entries from if need be. JAVA-5219
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved entries about the shared libraries before any other entries, anticipating that we'll reference this file from our docs as an example.
…-core` Without this metadata an application using the driver won't work, and there is no other place this metadata may come from, unless it is gathered via the GraalVM tracing agent. It's weird that we have to include reachability metadata specific to JDK, but there is nothing to do about it, other than saying that users are to collect the metadata for their applications themselves. The latter actually seems like the only right way to do this, but it conflicts with the fact that there is Oracle's reachability metadata repository (https://github.com/oracle/graalvm-reachability-metadata). JAVA-5219
Throwing from the constructor of `UnixServerAddress` seems better than from `SocketStreamFactory.create`. Not only this is more natural, but it also prevents the creation of a `MongoClient`, which wasn't he case previously. JAVA-5219
Entries were removed from `graalvm-native-image-app/src/main/resources/META-INF/native-image/reflect-config.json` as `NativeImageApp` works without them JAVA-5219
public final class UnixServerAddressSubstitution { | ||
@Substitute | ||
private static void checkNotInGraalVmNativeImage() { | ||
throw new UnsupportedOperationException("UnixServerAddress is not supported in GraalVM native image"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually achievable also via org.graalvm.nativeimage.ImageInfo.inImageRuntimeCode
instead of the substitutions mechanism. However, I decided to still go with the substitutions
- It's more powerful (not strictly so).
- It allows us to keep the GraalVM-related code in a separate package, though we still refer to in internal API documentation comments.
bson-record-codec/src/main/resources/META-INF/native-image/reflect-config.json
Outdated
Show resolved
Hide resolved
...r-core/src/main/com/mongodb/internal/graalvm/substitution/UnixServerAddressSubstitution.java
Show resolved
Hide resolved
@@ -0,0 +1,180 @@ | |||
[ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should all be removed now that they are in mongodb-crypt, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a ticket for that https://jira.mongodb.org/browse/JAVA-5408. But we can do it only when a new version of mongodb-crypt
is released, and the driver depends on that new version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leaving this unresolved since we agreed to keep this PR open until after mongodb-crypt release
driver-core/src/main/resources/META-INF/native-image/resource-config.json
Show resolved
Hide resolved
driver-core/src/main/resources/META-INF/native-image/reflect-config.json
Show resolved
Hide resolved
driver-core/src/main/resources/META-INF/native-image/reflect-config.json
Show resolved
Hide resolved
driver-core/src/main/resources/META-INF/native-image/reflect-config.json
Outdated
Show resolved
Hide resolved
driver-core/src/main/resources/META-INF/native-image/reflect-config.json
Outdated
Show resolved
Hide resolved
Goes to mongodb-crypt from driver-core/src/main/resources/META-INF/native-image/reflect-config.json: { "name":"boolean", "fields":[{"name":"OPTIONS"}, {"name":"STRING_ENCODING"}, {"name":"STRUCTURE_ALIGNMENT"}, {"name":"TYPE_MAPPER"}] }, { "name":"com.sun.crypto.provider.AESCipher$General", "methods":[{"name":"<init>","parameterTypes":[] }] }, { "name":"com.sun.crypto.provider.HmacCore$HmacSHA256", "methods":[{"name":"<init>","parameterTypes":[] }] }, { "name":"com.sun.crypto.provider.HmacCore$HmacSHA512", "methods":[{"name":"<init>","parameterTypes":[] }] }, { "name":"int", "fields":[{"name":"OPTIONS"}, {"name":"STRING_ENCODING"}, {"name":"STRUCTURE_ALIGNMENT"}, {"name":"TYPE_MAPPER"}] }, { "name":"java.lang.Throwable", "methods":[{"name":"addSuppressed","parameterTypes":["java.lang.Throwable"] }] }, { "name":"java.lang.reflect.Method", "methods":[{"name":"isVarArgs","parameterTypes":[] }] }, { "name":"java.nio.Buffer" }, { "name":"long", "fields":[{"name":"OPTIONS"}, {"name":"STRING_ENCODING"}, {"name":"STRUCTURE_ALIGNMENT"}, {"name":"TYPE_MAPPER"}] }, // NativePRNG also stays in driver-core { "name":"sun.security.provider.NativePRNG", "methods":[{"name":"<init>","parameterTypes":[] }, {"name":"<init>","parameterTypes":["java.security.SecureRandomParameters"] }] }, { "name":"sun.security.provider.SHA2$SHA256", "methods":[{"name":"<init>","parameterTypes":[] }] }, { "name":"sun.security.provider.SHA5$SHA512", "methods":[{"name":"<init>","parameterTypes":[] }] }, { "name":"void", "fields":[{"name":"OPTIONS"}, {"name":"STRING_ENCODING"}, {"name":"STRUCTURE_ALIGNMENT"}, {"name":"TYPE_MAPPER"}] }, Goes to mongodb-crypt from graalvm-native-image-app/src/main/resources/META-INF/native-image/reflect-config.json { "name":"org.slf4j.Logger" }, Goes to graalvm-native-image-app/build/native/agent-output/run/reflect-config.json from driver-core/src/main/resources/META-INF/native-image/reflect-config.json { "name":"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl", "methods":[{"name":"<init>","parameterTypes":[] }] }, { "name":"java.io.FilePermission" }, { "name":"java.lang.RuntimePermission" }, { "name":"java.net.NetPermission" }, { "name":"java.net.SocketPermission" }, { "name":"java.net.URLPermission", "methods":[{"name":"<init>","parameterTypes":["java.lang.String","java.lang.String"] }] }, { "name":"java.security.AllPermission" }, { "name":"java.security.SecurityPermission" }, { "name":"java.util.PropertyPermission" }, { "name":"java.util.concurrent.atomic.AtomicBoolean", "fields":[{"name":"value"}] }, { "name":"java.util.concurrent.atomic.AtomicReference", "fields":[{"name":"value"}] }, { "name":"javax.smartcardio.CardPermission" }, Goes to bson/src/main/resources/META-INF/native-image/reflect-config.json from driver-core/src/main/resources/META-INF/native-image/reflect-config.json { "name":"java.lang.Object", "queryAllDeclaredMethods":true }, Goes to bson/src/main/resources/META-INF/native-image/reflect-config.json from graalvm-native-image-app/src/main/resources/META-INF/native-image/reflect-config.json { "name":"org.slf4j.Logger" }, Goes to driver-core/src/main/resources/META-INF/native-image/reflect-config.json from graalvm-native-image-app/src/main/resources/META-INF/native-image/reflect-config.json { "name":"org.slf4j.Logger" }, JAVA-5219
LGTM except for the one that requires waiting for the next mongodb-crypt release. |
Converted to Draft PR while we await the mongodb-crypt release. |
FYI @stIncMale the mongodb-crypt release has been completed. |
…-crypt` from ``:driver-core` JAVA-5408
…`:bson` JAVA-5219
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At long last, LGTM!
I recommend reviewing the first four commits in this PR one at a time, as they are logically independent and don't conflict with each other.
native-image.properties
tosrc/main/resources/META-INF/native-image/native-image.properties
. Note how now they are immediately under thenative-image
folder.MongoClient
uses customcom.mongodb.spi.dns.DnsClientProvider
s in GraalVM viaServiceLoader
.:graalvm-native-image-app
into the relevant driver JARs.MongoClientSubstitutions
in Quarkus. The introduced substitutions achieve the following:4.1. Assert that substitutions are indeed used via
Substitutions.assertUsed
, which is called fromNativeImageApp
.4.2. Explicitly throw at run time if
UnixServerAddress
is used. It allegedly does not work with GraalVM, we'll figure this out (or not) in JAVA-5400.4.2.
Explicitly throw at run time if the Snappy compressor is used. I don't know why it allegedly does not work with GraalVM, we'll figure this out in JAVA-5383.Note that I omitted some of Quarkus substitutions for various reasons:
We should not substituteConnectionString.buildCompressors
such that it ignores the Snappy compressor. This method callsMongoCompressor.createSnappyCompressor
, which is substituted and throws an exception. Throwing an exception should be our reaction to the Snappy compressor in all scenarios.We should not substituteInternalStreamConnection.createCompressor
, as it can never be requested to create a Snappy compressor given thatMongoCompressor.createSnappyCompressor
is substituted and throws in GraalVM native image. Moreover, the Quarkus substitution makes it always throw, thus breaking all compressors in GraalVM.UnixServerAddress
.UnixServerAddress
: a substititution forSocketStreamFactory.create
, a deletion ofUnixSocketChannelStream
, a substitution forServerAddressHelper.createServerAddress
, we can substitute only the constructor ofUnixServerAddress
such that it throws.com.sun.jna.Native.getWebStartLibraryPath
.JAVA-5219
JAVA-5408