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

OptaPlanner extension #7049

Merged
merged 1 commit into from Feb 19, 2020
Merged

OptaPlanner extension #7049

merged 1 commit into from Feb 19, 2020

Conversation

ge0ffrey
Copy link
Contributor

@ge0ffrey ge0ffrey commented Feb 6, 2020

Works on GraalVM with -Dnative (see integration test).

@boring-cyborg boring-cyborg bot added area/core area/dependencies Pull requests that update a dependency file labels Feb 6, 2020
}
if (annotationInstances.isEmpty()) {
throw new IllegalStateException("No classes (" + convertAnnotationInstancesToString(annotationInstances)
+ ") found with a @" + PlanningSolution.class.getSimpleName() + " annotation.");
Copy link
Contributor Author

@ge0ffrey ge0ffrey Feb 6, 2020

Choose a reason for hiding this comment

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

We talked about removing this fail-fast in the previous PR, to follow hibernate-extension's approach, so I 'll do that in a next PR if that's ok, because of the open debate below.

Copy link
Contributor Author

@ge0ffrey ge0ffrey Feb 7, 2020

Choose a reason for hiding this comment

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

Open question, what is the behavior we want:
A) When there is no @PlanningSolution class, fail fast (current implementation, differs from hibernate extension)
B) When there is no @PlanningSolution class, only fail fast if there are any quarkus.optaplanner.* application.properties, otherwise do nothing
C) When there is no @PlanningSolution class, even if there are any quarkus.optaplanner.* application.properties, always do nothing quietly

Does de hibernate extension do B) or C)?

Copy link
Contributor Author

@ge0ffrey ge0ffrey Feb 7, 2020

Choose a reason for hiding this comment

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

I presume A) is a PITA when doing demo's where you grab something from code.quarkus.io and it doesn't "just run"?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah. I would avoid A). I would do C with a warning instead of staying quiet if some configuration properties are defined.

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.

Not a real review but a reminder that we need this to be properly squashed.

I think you can already squash most if not all of the commits prior to review.

@ge0ffrey

This comment has been minimized.

@ge0ffrey ge0ffrey requested a review from geoand February 7, 2020 09:17
@geoand
Copy link
Contributor

geoand commented Feb 7, 2020

One thing I see is that the module hasen't been added to ci-templates/stages.yml.

@ge0ffrey
Copy link
Contributor Author

ge0ffrey commented Feb 7, 2020

@geoand In stages.yml, can I just add it under "kogito" on line 380? Or should it have it's own template? How do I verify that my changes didn't break anything?

@geoand
Copy link
Contributor

geoand commented Feb 7, 2020

Yeah, just add it under kogito and it should be run by CI automatically :)

You'll be able to verify from the name of the job - it will include your module

@ge0ffrey
Copy link
Contributor Author

ge0ffrey commented Feb 7, 2020

@geoand By the way, I got the entire thing running in GraalVM without doing special tricks in the example for OptaPlanner. No custom solution cloner, etc :) It truly just works on GraalVM.
After this, we'll work on gizmo in the upcoming months to avoid reflection entirely. That's when we'll see an improvement in the thing that every OptaPlanner user is craving for: more CPU performance.

@geoand
Copy link
Contributor

geoand commented Feb 7, 2020

Excellent!

@ge0ffrey
Copy link
Contributor Author

ge0ffrey commented Feb 7, 2020

@gsmet @geoand Squashed, looking forward to the review (and merge hopefully). Thanks for all your support so far!

@geoand
Copy link
Contributor

geoand commented Feb 7, 2020

Thanks for the hard work @ge0ffrey!

We'll hopefully be able to review some time next week.

@ge0ffrey
Copy link
Contributor Author

Once this is merged, we'll work on the quickstart example that we'll build from scratch in a video about AI on quarkus. And we'll work on the guide of course :)

solverConfig = new SolverConfig(classLoader);
}
// The deployment classLoader must not escape to runtime
solverConfig.setClassLoader(null);
Copy link
Contributor

Choose a reason for hiding this comment

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

Just out of curiosity, what CL will be used at runtime as a result of this?

Copy link
Contributor Author

@ge0ffrey ge0ffrey Feb 12, 2020

Choose a reason for hiding this comment

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

Most of the time, it's not used, except if a scoreDRL is active.
If it's null in that case, ScoreDirectorFactoryConfig does this at runtime, during SolverFactory.buildSolver():

ClassLoader actualClassLoader = (classLoader != null) ? classLoader : getClass().getClassLoader();

Once we start doing more work during the static init, after we internalize the SolverConfig in the SolverFactory (currently impossible due to deprecated methods that will dissappear in 8.0), I 'll have to come back to this, as that code might become a problem then.
Good question indeed. :)

Copy link
Contributor

Choose a reason for hiding this comment

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

What am I sort of worried about is the dev-mode. There are certain situations where getClass().getClassLoader() might not give you what you expect.
Perhaps also adding QuarkusDevModeTest is a good idea to make sure things work properly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I 've been trying to add in QuarkusDevModeTest, but I reluctant to add a test that doesn't work in IntelliJ (it hurts productivity). In IntelliJ I get the "Undeclared build item class io.quarkus.deployment.builditem.ApplicationClassNameBuildItem" exception, but on the command line, I don't.

Copy link
Contributor

Choose a reason for hiding this comment

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

IntelliJ can mess up the classpath when running tests inside the Quarkus repo. I don't think it's something we need to worry about

Copy link
Member

Choose a reason for hiding this comment

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

Would it be possible to change that logic to use the TCCL if null?

Copy link
Contributor

@geoand geoand Feb 14, 2020

Choose a reason for hiding this comment

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

The dev mode test should prove that things are OK with this change.
To be 100% Quarkus compliant, upstream here should provide a flag to use the TCCL (of the time of execution) when the classloader is null, but I don't think we need it now

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using ThreadContextClassLoader is what it does by default if we don't set it to null.
When I remove the solverConfig.setClassLoader(null); line, I get

Caused by: java.lang.RuntimeException: Unable to serialize objects of type class io.quarkus.bootstrap.classloading.QuarkusClassLoader to bytecode as it has no default constructor
        at io.quarkus.deployment.recording.BytecodeRecorderImpl$53.createValue(BytecodeRecorderImpl.java:1273)

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jackson</artifactId>
<optional>true</optional>
Copy link
Contributor

Choose a reason for hiding this comment

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

Hm, have you tested to make sure this behaves the way you expect it to? And are you sure that OptaPlannerObjectMapperCustomizer is never loaded it the user doesn't add quarkus-jackson.

@gsmet WDYT about this pattern? I know it's not the way we have been doing things so far, but I do remember @ge0ffrey had a UX issue in mind when doing this, but I don't remember what it was :)

Copy link
Contributor Author

@ge0ffrey ge0ffrey Feb 12, 2020

Choose a reason for hiding this comment

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

I'll check if that customizer is loaded or not. It shouldn't be, but I indeed need to verify.

I am doing it this way, because:

  1. Having an extra extension quarkus-optaplanner-jackson-extension (and jaxb, jpa, etc), means users must explicitly add it. That's another hoop to jump through. If they forget to do that, the json marshalling of Score etc defaults to reflection way, which is hard to detect for new users.
  2. optaplanner-spring-boot-starter does it this way, it's automagic configuration. No hoop to jump through.

If you guys insist, we can switch to the normal pattern - or leave out the jackson integration entirely for now into a separate PR so the rest can be merged already.

Copy link
Member

Choose a reason for hiding this comment

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

Well, they would have to add the optaplanner Jackson dependency anyway, no? That's not very different from adding an extension.

I wouldn't make it a blocker to merge this PR if we are sure everything works when Jackson is not there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed by extracting new quarkus-optaplanner-jackson module

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 added some comments inline. I don't think there are a lot of blockers and we could probably merge a first step and then iterate.

The CL question and the Jackson exception should probably been sorted out before merging though.

solverConfig = new SolverConfig(classLoader);
}
// The deployment classLoader must not escape to runtime
solverConfig.setClassLoader(null);
Copy link
Member

Choose a reason for hiding this comment

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

Would it be possible to change that logic to use the TCCL if null?

}
if (annotationInstances.isEmpty()) {
throw new IllegalStateException("No classes (" + convertAnnotationInstancesToString(annotationInstances)
+ ") found with a @" + PlanningSolution.class.getSimpleName() + " annotation.");
Copy link
Member

Choose a reason for hiding this comment

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

Yeah. I would avoid A). I would do C with a warning instead of staying quiet if some configuration properties are defined.

// Fail fast during build to avoid a certain runtime failure
throw new IllegalStateException(
"When using both Jackson and OptaPlanner,"
+ " add a dependency on org.optaplanner:optaplanner-persistence-jackson too.",
Copy link
Member

Choose a reason for hiding this comment

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

What annoys me a bit here is that if you use Jackson for your REST services and OptaPlanner without Jackson persistence, you end up with this exception. Or did I misunderstand?

Copy link
Contributor Author

@ge0ffrey ge0ffrey Feb 14, 2020

Choose a reason for hiding this comment

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

Yea - the idea is that it would stick out like a soar thumb, fail-fast basically. While if you forgot to add quarkus-optaplanner-jackson, there is no fail fast: it's just hard to notice that score instances aren't being jacksoned correctly.
Anyway, not worth the hassle - I am creating quarkus-optaplanner-jackson to align with the quarkus ecosystem spirit :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


// Not named OptaPlannerConfig because classes ending with just "Config" collide with OptaPlanner's API
@ConfigRoot(name = "optaplanner")
public class OptaPlannerQuarkusConfig {
Copy link
Member

Choose a reason for hiding this comment

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

Call it OptaPlannerBuildTimeConfig and get rid of the comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, good point.

* Other options include a number or formula based on the available processor count.
*/
@ConfigItem
Optional<String> parallelSolverCount;
Copy link
Member

Choose a reason for hiding this comment

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

That looks like a runtime property to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It only affects SolverManager during build time - those get injected, but we don't need to read this property at runtime.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Discussed with Georgios. He agrees, these are build time properties (they get translated into optaplanner configuration during the build time, they aren't used at runtime). Renamed class to end with BuildTimeConfig instead of QuarkusConfig to signal that.

Copy link
Member

Choose a reason for hiding this comment

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

Well, they might be build time for your code but I think it's typically values that you will want to change at runtime because it might depend on the hardware on which you deploy it.

I don't have a problem merging that as is for now but I think it's probably something we should think about for a later version (but it might be totally impractical, I don't know OptaPlanner at all).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed - or users wanting to override them with -D system properties?

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jackson</artifactId>
<optional>true</optional>
Copy link
Member

Choose a reason for hiding this comment

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

Well, they would have to add the optaplanner Jackson dependency anyway, no? That's not very different from adding an extension.

I wouldn't make it a blocker to merge this PR if we are sure everything works when Jackson is not there.

extensions/optaplanner/runtime/pom.xml Show resolved Hide resolved
@ge0ffrey
Copy link
Contributor Author

ge0ffrey commented Feb 14, 2020

@gsmet I've separated the jackson stuff into a new module called quarkus-optaplanner-jackson. (one day we'll also add quarkus-optaplanner-jaxb)

@ge0ffrey
Copy link
Contributor Author

I think all review comments are handled (many adjustments done, accepted all majors and few minors, rejected a few minors), except for the fail-fast "use plan C" issue, which is for a later PR.

Let me know if there's anything left blocking the merge on this PR :)

@gastaldi gastaldi added the triage/needs-rebase This PR needs to be rebased first because it has merge conflicts label Feb 15, 2020
@gastaldi
Copy link
Contributor

Can you please squash your commits into a single one? Also it seems you need to rebase since there are conflicts from recent changes to master

@ge0ffrey
Copy link
Contributor Author

@gastaldi @gsmet @geoand Rebased and squashed :)

@geoand
Copy link
Contributor

geoand commented Feb 17, 2020

The CI logs from the latest run are here.

The issue appears to have anything to do with this PR:

2020-02-17T13:00:43.0485880Z [INFO] -------------------------------------------------------
2020-02-17T13:00:43.0488837Z [INFO]  T E S T S
2020-02-17T13:00:43.0494363Z [INFO] -------------------------------------------------------
2020-02-17T13:00:44.3205589Z [INFO] Running io.quarkus.it.keycloak.CodeFlowTest
2020-02-17T13:00:55.8238287Z 2020-02-17 13:00:55,820 INFO  [io.quarkus] (main) Quarkus 999-SNAPSHOT started in 4.156s. Listening on: http://0.0.0.0:8081
2020-02-17T13:00:55.8242324Z 2020-02-17 13:00:55,823 INFO  [io.quarkus] (main) Profile test activated. 
2020-02-17T13:00:55.8246599Z 2020-02-17 13:00:55,824 INFO  [io.quarkus] (main) Installed features: [cdi, oidc, resteasy, resteasy-jackson, security]
2020-02-17T13:01:17.8245822Z [ERROR] Tests run: 8, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 33.494 s <<< FAILURE! - in io.quarkus.it.keycloak.CodeFlowTest
2020-02-17T13:01:17.8247068Z [ERROR] testAccessTokenInjection  Time elapsed: 1.485 s  <<< ERROR!
2020-02-17T13:01:17.8288543Z com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException: Too much redirect for http://localhost:8081/web-app?state=3196cb35-59a8-4c46-9acf-f41e0ae6fd4c&session_state=09b2a60c-aa2e-452e-91c0-554d4aa9adc9&code=1d187eb5-cce0-41af-a2f1-904f2b04492c.09b2a60c-aa2e-452e-91c0-554d4aa9adc9.66d410fa-16a7-4bf6-a957-4cf37a3e0911

So I'll restart CI

@geoand
Copy link
Contributor

geoand commented Feb 18, 2020

CI error is:

2020-02-18T08:54:30.6540679Z [ERROR] [ERROR] Could not find the selected project in the reactor: integration-tests/optaplanner @ 
2020-02-18T08:54:30.6577421Z [ERROR] Could not find the selected project in the reactor: integration-tests/optaplanner -> [Help 1]

Seems like a problem with the PR

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.

Raaah, forgot to validate my review.

bom/runtime/pom.xml Outdated Show resolved Hide resolved
extensions/optaplanner-jackson/runtime/pom.xml Outdated Show resolved Hide resolved
test.modifyResourceFile("solverConfig.xml", s -> s.replace("<secondsSpentLimit>2</secondsSpentLimit>",
"<secondsSpentLimit>9</secondsSpentLimit>"));
resp = RestAssured.get("/solver-config/seconds-spent-limit").asString();
Assertions.assertEquals("secondsSpentLimit=9", resp);
Copy link
Member

Choose a reason for hiding this comment

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

I'm curious how this works without a LiveReloadBuildItem? I think we should probably have one to restart all the app in case of a change, similar to what we do elsewhere.

Copy link
Contributor

Choose a reason for hiding this comment

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

A HotDeploymentWatchedFileBuildItem has been added to the PR that enables the config file for reload.

ci-templates/stages.yml Outdated Show resolved Hide resolved
@gsmet
Copy link
Member

gsmet commented Feb 18, 2020

@geoand yes it is, thanks for your message because, apparently, I forgot to post my last review from yesterday. The name of the module is incorrect in the pipeline file.

@gsmet
Copy link
Member

gsmet commented Feb 18, 2020

@ge0ffrey I think we're really not far from having this in a mergeable state. Let's try to get this in by Friday!

@ge0ffrey
Copy link
Contributor Author

ge0ffrey commented Feb 18, 2020

@gsmet Thanks for the review. All comments applied except the LiveReloadBuildItem one, because we use HotDeploymentWatchedFileBuildItem (see Georgios's comment) and we have a hot deploy test that works.

Ready for another review. Let me know if there's anything else that needs to change.

@gsmet gsmet merged commit 384ed5b into quarkusio:master Feb 19, 2020
@gsmet
Copy link
Member

gsmet commented Feb 19, 2020

Merged, thanks a lot for this effort. Can you work on preparing the guide and the quickstart? Thanks!

@ge0ffrey
Copy link
Contributor Author

Coming soon :)

@gsmet gsmet added this to the 1.3.0 milestone Feb 28, 2020
@lordofthejars
Copy link
Contributor

@ge0ffrey @gsmet I am missing an adoc guide so everyone is aware that the support of OptaPlanner is there.

@machi1990
Copy link
Member

@ge0ffrey @gsmet I am missing an adoc guide so everyone is aware that the support of OptaPlanner is there.

Hi @lordofthejars there is a PR here #7579 to add the guide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/core area/dependencies Pull requests that update a dependency file release/noteworthy-feature triage/needs-rebase This PR needs to be rebased first because it has merge conflicts
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants