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

Kafka Snappy library fails to load when restarting Quarkus in the same JVM #39767

Closed
Empty-Glasss opened this issue Mar 28, 2024 · 5 comments · Fixed by #40304
Closed

Kafka Snappy library fails to load when restarting Quarkus in the same JVM #39767

Empty-Glasss opened this issue Mar 28, 2024 · 5 comments · Fixed by #40304
Labels
area/kafka env/windows Impacts Windows machines kind/bug Something isn't working triage/backport-3.8
Milestone

Comments

@Empty-Glasss
Copy link

Empty-Glasss commented Mar 28, 2024

Describe the bug

I have a project where the tests run with multiple test profiles, which causes a new Quarkus instance for each group of tests (but I believe it's still the same JVM instance). However, the Snappy library that is being loaded cannot be loaded the second time since there is still a lock on the snappyjava.dll file (I think the OS keeps a lock since it's loaded in as a DLL)

Expected behavior

Snappy loads successfully each Quarkus startup

Actual behavior

The first time Quarkus starts up successfully. The second time Quarkus starts up, I get the following exception:

java.lang.RuntimeException: java.lang.RuntimeException: Failed to start quarkus
	at io.quarkus.test.junit.QuarkusTestExtension.throwBootFailureException(QuarkusTestExtension.java:638)
	at io.quarkus.test.junit.QuarkusTestExtension.interceptTestClassConstructor(QuarkusTestExtension.java:722)
	at java.base/java.util.Optional.orElseGet(Optional.java:364)
	at java.base/java.util.Optional.orElseGet(Optional.java:364)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(AbstractList.java:722)
	at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762)
	at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:1006)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276)
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Caused by: java.lang.RuntimeException: Failed to start quarkus
	at io.quarkus.runner.ApplicationImpl.doStart(Unknown Source)
	at io.quarkus.runtime.Application.start(Application.java:101)
	at java.base/java.lang.reflect.Method.invoke(Method.java:578)
	at io.quarkus.runner.bootstrap.StartupActionImpl.run(StartupActionImpl.java:285)
	at io.quarkus.test.junit.QuarkusTestExtension.doJavaStart(QuarkusTestExtension.java:252)
	at io.quarkus.test.junit.QuarkusTestExtension.ensureStarted(QuarkusTestExtension.java:605)
	at io.quarkus.test.junit.QuarkusTestExtension.beforeAll(QuarkusTestExtension.java:655)
	... 1 more
Caused by: java.io.UncheckedIOException: Unable to extract native library snappyjava.dll to C:\Users\<user>\AppData\Local\Temp\snappyjava.dll
	at io.quarkus.kafka.client.runtime.SnappyRecorder.extractLibraryFile(SnappyRecorder.java:54)
	at io.quarkus.kafka.client.runtime.SnappyRecorder.loadSnappy(SnappyRecorder.java:30)
	at io.quarkus.deployment.steps.KafkaProcessor$loadSnappyIfEnabled852439731.deploy_0(Unknown Source)
	at io.quarkus.deployment.steps.KafkaProcessor$loadSnappyIfEnabled852439731.deploy(Unknown Source)
	... 8 more
Caused by: java.io.FileNotFoundException: C:\Users\<user>\AppData\Local\Temp\snappyjava.dll (The process cannot access the file because it is being used by another process)
	at java.base/java.io.FileOutputStream.open0(Native Method)
	at java.base/java.io.FileOutputStream.open(FileOutputStream.java:295)
	at java.base/java.io.FileOutputStream.<init>(FileOutputStream.java:236)
	at java.base/java.io.FileOutputStream.<init>(FileOutputStream.java:185)
	at io.quarkus.kafka.client.runtime.SnappyRecorder.extractLibraryFile(SnappyRecorder.java:46)
	... 11 more

How to Reproduce?

  1. Create a Quarkus 3.8.2 Maven project with a dependency to quarkus-kafka-client and quarkus.kafka.snappy.enabled=true
  2. Create 2 tests, each with @QuarkusTest and a different @TestProfile
  3. Run the tests, for example with mvn clean install

Output of uname -a or ver

Microsoft Windows [Version 10.0.22631.3296]

Output of java -version

openjdk 20.0.1 2023-04-18 OpenJDK Runtime Environment (build 20.0.1+9-29) OpenJDK 64-Bit Server VM (build 20.0.1+9-29, mixed mode, sharing)

Quarkus version or git rev

3.8.2

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

Apache Maven 3.9.4 (dfbb324ad4a7c8fb0bf182e6d91b0ae20e3d2dd9)

Additional information

No response

@Empty-Glasss Empty-Glasss added the kind/bug Something isn't working label Mar 28, 2024
@quarkus-bot quarkus-bot bot added area/kafka env/windows Impacts Windows machines labels Mar 28, 2024
Copy link

quarkus-bot bot commented Mar 28, 2024

/cc @alesj (kafka), @cescoffier (kafka), @ozangunalp (kafka)

@cescoffier cescoffier self-assigned this Apr 22, 2024
cescoffier added a commit to cescoffier/quarkus that referenced this issue Apr 26, 2024
This commit introduces a solution for loading the Snappy native library across multiple test profiles. Due to the constraint that native libraries can only be loaded from a single classloader, a shared classloader is now utilized for loading Snappy in test mode.

Please note, this feature is exclusively applicable when running tests.

Fixes: quarkusio#39767
@cescoffier
Copy link
Member

Java has the restriction that a native library can only be loaded by a single classloader. Unfortunately, we cannot change that behavior. When using a test profile, you end up with 2 classloaders, which leads to the issue.

I've added a way to handle this situation in #40304. You would need to add the following property to your application.properties file:

quarkus.kafka.snappy.load-from-shared-classloader=true

@cescoffier cescoffier removed their assignment Apr 26, 2024
@quarkus-bot quarkus-bot bot added this to the 3.11 - main milestone Apr 30, 2024
holly-cummins pushed a commit to holly-cummins/quarkus that referenced this issue Jul 31, 2024
This commit introduces a solution for loading the Snappy native library across multiple test profiles. Due to the constraint that native libraries can only be loaded from a single classloader, a shared classloader is now utilized for loading Snappy in test mode.

Please note, this feature is exclusively applicable when running tests.

Fixes: quarkusio#39767
@lorenzobenvenuti
Copy link

Java has the restriction that a native library can only be loaded by a single classloader. Unfortunately, we cannot change that behavior. When using a test profile, you end up with 2 classloaders, which leads to the issue.

I've added a way to handle this situation in #40304. You would need to add the following property to your application.properties file:

quarkus.kafka.snappy.load-from-shared-classloader=true

Hi, will this be backported to 3.8 (we want to stick with LTS versions)?

Thanks,

lorenzo

@cescoffier
Copy link
Member

I would consider that as a feature so normally we do not backport them. But, it can also be seen as a bug fix.

It should be backportable - and let's see if it can be done.

Note that 3.15 is the next LTS, and will be released soonish (CR1 is already out)

Copy link

quarkus-bot bot commented Sep 12, 2024

@cescoffier triage/backport* labels may not be added to an issue. Please add them to the corresponding pull request.

This message is automatically generated by a bot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/kafka env/windows Impacts Windows machines kind/bug Something isn't working triage/backport-3.8
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants