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

Native: Brotli4J compression requires explicit config #40533

Closed
jcarranzan opened this issue May 9, 2024 · 12 comments
Closed

Native: Brotli4J compression requires explicit config #40533

jcarranzan opened this issue May 9, 2024 · 12 comments
Assignees
Labels
area/native-image kind/bug Something isn't working

Comments

@jcarranzan
Copy link
Contributor

jcarranzan commented May 9, 2024

Describe the bug

I've been doing a Quarkus Http Brotli4J test coverage development and the tests were passing ok in different environments also in Openshift tests were ok. However, it just gave me issues in Openshift with native mode execution.

  • Context execution found in:

    RHEL8 / JDK 17 / Run OpenShift tests in native mode

  • native image used:
    <quarkus.s2i.base-native-image>quay.io/quarkus/ubi-quarkus-native-binary-s2i:2.0</quarkus.s2i.base-native-image>

  • Quarkus platform version:
    3.10.0

This is my reproducer:
https://github.com/jcarranzan/quarkus-reproducer/tree/brotli4j-native
This is the maven command execution:
mvn clean verify -Dnative -Popenshift -Dfailsafe.failIfNoSpecifiedTests=false -Dit.test="OpenShiftBrotli4JIT"

Error logs:

  10:24:27,158 INFO  ## Running test OpenShiftBrotli4JIT.checkTextPlainWithtBrotli4J()
12:24:30  10:24:30,075 INFO  [app] 10:24:27,207 Failed to initialize a channel. Closing: [id: 0x832a69a5, L:embedded - R:embedded]: io.netty.channel.ChannelPipelineException: io.netty.handler.codec.compression.BrotliEncoder.handlerAdded() has thrown an exception; removed.
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.DefaultChannelPipeline.callHandlerAdded0(DefaultChannelPipeline.java:624)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.DefaultChannelPipeline.addLast(DefaultChannelPipeline.java:223)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.DefaultChannelPipeline.addLast(DefaultChannelPipeline.java:381)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.DefaultChannelPipeline.addLast(DefaultChannelPipeline.java:370)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.embedded.EmbeddedChannel$2.initChannel(EmbeddedChannel.java:222)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.ChannelInitializer.initChannel(ChannelInitializer.java:129)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.ChannelInitializer.handlerAdded(ChannelInitializer.java:112)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannelHandlerContext.callHandlerAdded(AbstractChannelHandlerContext.java:1130)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.DefaultChannelPipeline.callHandlerAdded0(DefaultChannelPipeline.java:609)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.DefaultChannelPipeline.access$100(DefaultChannelPipeline.java:46)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.DefaultChannelPipeline$PendingHandlerAddedTask.execute(DefaultChannelPipeline.java:1463)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.DefaultChannelPipeline.callHandlerAddedForAllHandlers(DefaultChannelPipeline.java:1115)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.DefaultChannelPipeline.invokeHandlerAddedIfNeeded(DefaultChannelPipeline.java:650)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannel$AbstractUnsafe.register0(AbstractChannel.java:514)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannel$AbstractUnsafe.register(AbstractChannel.java:480)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.embedded.EmbeddedChannel$EmbeddedUnsafe$1.register(EmbeddedChannel.java:840)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.embedded.EmbeddedEventLoop.register(EmbeddedEventLoop.java:181)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.embedded.EmbeddedEventLoop.register(EmbeddedEventLoop.java:175)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.embedded.EmbeddedChannel.setup(EmbeddedChannel.java:227)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.embedded.EmbeddedChannel.<init>(EmbeddedChannel.java:204)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.handler.codec.http.HttpContentCompressor.beginEncode(HttpContentCompressor.java:275)
12:24:30  10:24:30,075 INFO  [app] 	at io.vertx.core.http.impl.HttpChunkContentCompressor.beginEncode(HttpChunkContentCompressor.java:51)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.handler.codec.http.HttpContentEncoder.encode(HttpContentEncoder.java:163)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.handler.codec.http.HttpContentEncoder.encode(HttpContentEncoder.java:57)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.handler.codec.MessageToMessageCodec$1.encode(MessageToMessageCodec.java:67)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.handler.codec.MessageToMessageEncoder.write(MessageToMessageEncoder.java:90)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.handler.codec.MessageToMessageCodec.write(MessageToMessageCodec.java:116)
12:24:30  10:24:30,075 INFO  [app] 	at io.vertx.core.http.impl.HttpChunkContentCompressor.write(HttpChunkContentCompressor.java:46)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:891)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:875)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:984)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:868)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.handler.stream.ChunkedWriteHandler.write(ChunkedWriteHandler.java:140)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:891)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:875)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:984)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:868)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.handler.timeout.IdleStateHandler.write(IdleStateHandler.java:305)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:891)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:875)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:984)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:868)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.ChannelDuplexHandler.write(ChannelDuplexHandler.java:115)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtensionHandler.onHttpResponseWrite(WebSocketServerExtensionHandler.java:212)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtensionHandler.write(WebSocketServerExtensionHandler.java:169)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:891)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannelHandlerContext.invokeWriteAndFlush(AbstractChannelHandlerContext.java:956)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:982)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.AbstractChannelHandlerContext.writeAndFlush(AbstractChannelHandlerContext.java:950)
12:24:30  10:24:30,075 INFO  [app] 	at io.vertx.core.net.impl.ConnectionBase.write(ConnectionBase.java:181)
12:24:30  10:24:30,075 INFO  [app] 	at io.vertx.core.net.impl.ConnectionBase.lambda$queueForWrite$2(ConnectionBase.java:247)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:173)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:166)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569)
12:24:30  10:24:30,075 INFO  [app] 	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
12:24:30  10:24:30,076 INFO  [app] 	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
12:24:30  10:24:30,076 INFO  [app] 	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
12:24:30  10:24:30,076 INFO  [app] 	at java.base@21.0.2/java.lang.Thread.runWith(Thread.java:1596)
12:24:30  10:24:30,076 INFO  [app] 	at java.base@21.0.2/java.lang.Thread.run(Thread.java:1583)
12:24:30  10:24:30,076 INFO  [app] 	at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:833)
12:24:30  10:24:30,076 INFO  [app] 	at org.graalvm.nativeimage.builder/com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:211)
12:24:30  10:24:30,076 INFO  [app] Caused by: java.lang.UnsatisfiedLinkError: com.aayushatharva.brotli4j.encoder.EncoderJNI.nativeCreate([J)Ljava/nio/ByteBuffer; [symbol: Java_com_aayushatharva_brotli4j_encoder_EncoderJNI_nativeCreate or Java_com_aayushatharva_brotli4j_encoder_EncoderJNI_nativeCreate___3J]
12:24:30  10:24:30,076 INFO  [app] 	at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.access.JNINativeLinkage.getOrFindEntryPoint(JNINativeLinkage.java:152)
12:24:30  10:24:30,076 INFO  [app] 	at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.JNIGeneratedMethodSupport.nativeCallAddress(JNIGeneratedMethodSupport.java:54)
12:24:30  10:24:30,076 INFO  [app] 	at com.aayushatharva.brotli4j.encoder.EncoderJNI.nativeCreate(Native Method)
12:24:30  10:24:30,076 INFO  [app] 	at com.aayushatharva.brotli4j.encoder.EncoderJNI.access$200(EncoderJNI.java:17)
12:24:30  10:24:30,076 INFO  [app] 	at com.aayushatharva.brotli4j.encoder.EncoderJNI$Wrapper.<init>(EncoderJNI.java:90)
12:24:30  10:24:30,076 INFO  [app] 	at com.aayushatharva.brotli4j.encoder.Encoder.<init>(Encoder.java:48)
12:24:30  10:24:30,076 INFO  [app] 	at com.aayushatharva.brotli4j.encoder.BrotliEncoderChannel.<init>(BrotliEncoderChannel.java:38)
12:24:30  10:24:30,076 INFO  [app] 	at com.aayushatharva.brotli4j.encoder.BrotliEncoderChannel.<init>(BrotliEncoderChannel.java:50)
12:24:30  10:24:30,076 INFO  [app] 	at io.netty.handler.codec.compression.BrotliEncoder$Writer.<init>(BrotliEncoder.java:193)
12:24:30  10:24:30,076 INFO  [app] 	at io.netty.handler.codec.compression.BrotliEncoder$Writer.<init>(BrotliEncoder.java:185)
12:24:30  10:24:30,076 INFO  [app] 	at io.netty.handler.codec.compression.BrotliEncoder.handlerAdded(BrotliEncoder.java:102)
12:24:30  10:24:30,076 INFO  [app] 	at io.netty.channel.AbstractChannelHandlerContext.callHandlerAdded(AbstractChannelHandlerContext.java:1130)
12:24:30  10:24:30,076 INFO  [app] 	at io.netty.channel.DefaultChannelPipeline.callHandlerAdded0(DefaultChannelPipeline.java:609)
12:24:30  10:24:30,076 INFO  [app] 	... 61 more

Expected behavior

Works properly

Actual behavior

Tests failed because of the error mentioned above in the description.

How to Reproduce?

  1. git clone -b brotli4j-native https://github.com/jcarranzan/quarkus-reproducer.git
  2. Login in an openshift cluster
  3. Execution in Openshift with native profile:
    mvn clean verify -Dnative -Popenshift -Dfailsafe.failIfNoSpecifiedTests=false -Dit.test="OpenShiftBrotli4JIT"
  4. Check the logs

Output of uname -a or ver

Linux Fedora 39 x86_64

Output of java -version

openjdk version "17.0.9" 2023-10-17

Mandrel or GraalVM version (if different from Java)

No response

Quarkus version or git rev

No response

Build tool (ie. output of mvnw --version or gradlew --version)

No response

Additional information

The issue was found in Openshift but also is produced in baremetal with native mode execution.
No response

@quarkus-bot
Copy link

quarkus-bot bot commented May 9, 2024

/cc @Karm (mandrel), @galderz (mandrel), @geoand (openshift), @iocanel (openshift), @zakkak (mandrel,native-image)

@jedla97
Copy link
Contributor

jedla97 commented May 9, 2024

Just a note. This is also happening on baremetal. You can check it with provided reproducer by running mvn clean verify -Dnative

  1. mvn install -Dnative
  2. ./target/reproducer-with-quarkus-1.0.0-SNAPSHOT-runner
  3. Go to http://127.0.0.1:8080/compression/text and see quarkus log

@Karm Karm self-assigned this May 9, 2024
@Karm
Copy link
Member

Karm commented May 13, 2024

Hello @jcarranzan,

I have your reproducer working properly in native. Let me polish the how-to and share it with you.

This is neither Quarkus nor GraalVM issue per se. It's just that AOT requires some more config work to get it working.
Quarkus extensions shield users from this complexity, but since there is none for this specific library usage (or none I know of), you need to add some manual configs.

@Karm Karm changed the title Issue found with Brotli4J compression run in Openshift in native mode Native: Brotli4J compression requires explicit config May 13, 2024
@Karm
Copy link
Member

Karm commented May 13, 2024

@jcarranzan

Here is a working example, tested with Quarkus 3.10.0 on Linux aarch64 (see README.md) and Linux amd64, using Mandrel native-image, Mandrel-23.1.3.1-Final (JDK 21.0.3+9-LTS).

See:
https://github.com/Karm/dev-null/tree/main/brotli4j-native

The example is heavily simplified version of your original reproducer, because the issue has nothing to do with either OpenShift or some other dependencies you had there.

You don't need to do anything in the Java code, you just need to mark Brotli4jLoader.java for runtime init and you need to specify resource config so as libbrotli.so gets bundled in the final executable file as a resource.

See application.properties.

@Karm Karm closed this as completed May 13, 2024
@gsmet
Copy link
Member

gsmet commented May 13, 2024

Is this usage widespread? Would it make sense to develop a small extension similar to what I did here: https://github.com/quarkiverse/quarkus-opencv/ ?

@Karm
Copy link
Member

Karm commented May 13, 2024

@gsmet https://quarkusio.zulipchat.com/#narrow/stream/187038-dev/topic/Webserver.20compression/near/438350071

I'd say yes. Unless I misunderstood and it's already a part of e.g. Vert.x.

@cescoffier
Copy link
Member

I would not say 'widespread'. It's starting to be used. Vert.x already has the support (via netty), and due to some ugly substitution business, we have it on the classpath.

@Karm Based on your exploration, I would do the same thing I did for RocksDB, Snappy... If a built-time property enables Brotli, we include the appropriate native library into the native executable and mark Broli4Loarder as runtime init.

@melloware
Copy link
Contributor

Should we re-open this ticket then?

@Karm
Copy link
Member

Karm commented May 15, 2024

@melloware @cescoffier

Should we re-open this ticket then?

I don't know. Not a bug... it's a missing feature :)

I will work on the seamless integration regardless of this particular issue being open or not.

@cescoffier
Copy link
Member

I guess the next to be PR should be enough. This ticket provides the workaround (correct instructions).

@melloware
Copy link
Contributor

@Karm thanks i will monitor for PR's!

@michalvavrik
Copy link
Contributor

@Karm thanks i will monitor for PR's!

yeah, we also need to know to change our tests, so let's all monitor instead of having a tracker that will notify us

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/native-image kind/bug Something isn't working
Projects
None yet
Development

No branches or pull requests

7 participants