Replies: 4 comments 4 replies
-
@thimmwork Interesting! We definitely intend to support Spring in Native mode. Your write-up will come in handy. |
Beta Was this translation helpful? Give feedback.
-
@thimmwork Great writeup! Sounds like we want that AotSolverFactoryConfig logic in the spring autoconfigure plugin? |
Beta Was this translation helpful? Give feedback.
-
Issue: #547 |
Beta Was this translation helpful? Give feedback.
-
"Gizmo does not work well with Kotlin due to Kotlin's property convention" |
Beta Was this translation helpful? Give feedback.
-
Hi everyone,
I took a look at Spring Boot 3 native images and managed to migrate an existing service that uses Timefold.
Probably there are others out there who could benefit from this, so let me list the steps I had to do:
Build using GraalVM
I'm on a Mac, so I used sdkman and
sdk use java 21.0.1-graalce
.The build process runs docker images, so I used
brew install colima
andcolima start
to set up colima as docker drop-in replacement.In addition, I had to create symbolic links like this to make colima available to the build plugin:
sudo ln -s ~/.colima/docker.sock /var/run/docker.sock && ln -s ~/.colima/docker.sock ~/.docker/docker.sock
My project is a maven project, so I used maven 3.9.5 and
mvn -Pnative clean package
to build.Also, I used graalvm's native-maven-plugin instead of the spring-boot-plugin's build-image goal. The spring-boot-plugin was hard to satisfy due to limitation to a small set of exact supported JDK minor versions.
I've set an environement variable, though I'm not sure whether it has an effect:
Maven profile
Spring Boot comes with a preconfigured profile
native
, but I had to add this snippet to get useful error messages in case native compilation fails:I don't want to build the native image every time I compile locally, so the profile is not active by default, but specifically activated by the CI/CD pipeline.
The first buildArg will provide you with more detailed information in case errors occur. The second buildArg creates a short json that summarizes build time and used resources. This may come handy when provisioning CI/CD runners etc.
Configure Gizmo
Fortunately, I already switched to Gizmo some months ago.
Gizmo needs planning variables to be public fields.
It doesn't interact well with Kotlin due to Kotlin's property convention, so I ended up rewriting the Solution, PlanningEntities and PlanningFacts in Java. After all, Java is an island within my codebase, surrounded by a sea of Kotlin ;)
As described in Timefold's documentation, add this line to your
solverConfig.xml
:and make sure you have
on your classpath.
Create solverFactory ahead of time
Creating the SolverFactory triggers Gizmo which needs to run ahead of time because it uses the Java ByteCode compiler to generate code.
I used this class:
In my
@Service
class, I'm using constructor injection to get a supplierand get the actual solver instance with
Unit Tests
In unit tests, I initialize my
@Service
class by calling the constructor with a self-instantiated instance:JaCoCo code coverage
My service uses JaCoCo for test coverage checks. The classes generated by Spring generate false-positives. Though this is not Timefold-related, the generated classes may very carry your Timefold-related bean factory names.
Add this to your jacoco plugin/configuration/exclusions to ignore coverage for the generated classes:
Additional metadata
When running the native binary, there was still some reflection that failed. When I ran
the java agent generated additional metadata into my project's
src/main/resources/META-INF/
folder.This folder is then automatically used in the next compile run to add reflection information for the respective classes to the native image.
I had to run this a couple of times to get it to completion, which is why I added the files to git. They will rarely change in the future for my service. Make sure you trigger all relevant code paths, especially a Timefold solver run (and other paths using dependencies beyond the spring boot ecosystem).
Statistics
The folder META-INF takes up 324 kB.
My project takes 0m56s to compile a jar of 96 MB
and 6m01s with native profile to compile a binary of 212 MB.
Credit
Quite some credit goes to https://www.baeldung.com/spring-native-intro
Have you used native images yet?
I would like to hear your thoughts and experience.
Beta Was this translation helpful? Give feedback.
All reactions