Got a problem of persistence, that the system persist a different value from the committed one.
I tried to use optaplanner in cuba, and built three main entities, i.e. CloudBalancing, Computer and Process. To the end, I should get a solution which distributes Processes to different Computers. Suppose there are two processes(P1, P2) and two computers(A, B), with optaplanner and its calculator from official examples, I got the solution that P1, P2 should both assigned to A. The result before I commit it, indeed is correct from the memory perspective. But after it’s committed, one of the processes will be assigned to computer B, that is wired.
1. Investigation
So I tried to debug and locate where the value was changed by EclipseLink. Below is the result where I can do the best.
Hi,
I tried your project, and it works fine for me.
After solver.solve(problem) one process gets A, second process gets B.
And both entities are saved to the database as expected.
If you still experience the bug, please create a reproducible test case.
You can create a middleware test and easily move your logic from com.hh.optaplanner.service.ResolveServiceBean#resolve method to this test, and debug it there. Debugging a test is quicker than invoking actions from UI.
As I see, you pass loaded entities to the solver engine, and it modifies entities passed (read and sets computer association fields, reflectively).
I have two theories why you might experience such glitches:
If solver engine uses multi-threading in any way, it can cause problems. Because JPA entities shouldn’t be simultaneously modified from several threads.
If solver engine assigns values other then by invoking getter and setter - modifies field value directly - it can also cause problems with entities.
So if I were on your place, I would try to create separate POJO objects and use them in the optimization engine, then copy attributes from loaded entities before solving and copying changed attributes back to entities after solving.
Secondly my expect is that two processes are both assigned to A, that will pay lowest for computer maintenance. And the solver engine gives the right answer, even before I commit, they actually are assigned to A in memory, however, after committed, one process gets B.
At last, your solution to create separate POJOs is a good choice, I will try.
But what I am not understood is whether JPA will check and prevent entities modification from different threads. In my opinion, if I modify entities in multi-threads, below is a normal case.
suppose we have two threads, each get a same entity via same id using datamanager simultaneously, both threads get a detached one.
update both detached entities in two threads
commit one of them, JPA will merge it into the managed one successfully.
commit another of them, JPA will prevent it and throw exception said it is already changed.