Optaplanner 7.9.0 и добавление многопоточности: одно и то же исключение PlanningId

Некоторое время у меня есть проект optaplanner, который распределяет задания по списку рабочих с использованием различных свойств оценки, написанных на java. Довольный тем, как это работает, я решил обновить с 7.7.0 до 7.9.0, включить новую опцию многопоточности и посмотреть, будет ли она работать быстрее. Однако теперь я получаю следующую ошибку:

Исключение в потоке «Thread-8» java.lang.IllegalStateException: поток перемещения с moveThreadIndex (0) вызвал исключение. Ретранслируется здесь, в родительском потоке. Вызвано: java.lang.IllegalArgumentException: невозможно найти externalObject (... Worker @ 72d2327a). Можно дать классу (класс Worker) аннотацию PlanningId или изменить LookUpStrategyType аннотации PlanningSolution или не полагаться на функциональность, которая зависит от ScoreDirector.lookUpWorkingObject ().

Итак, я попытался добавить @PlanningId к уникальному идентификатору рабочего класса и получил:

Исключение в потоке «Thread-7» java.lang.IllegalStateException: рабочие объекты (Worker @ 4438b624, Worker @ 4438b624) имеют одинаковый идентификатор планирования ((class Worker, 50)). Рабочие объекты должны быть уникальными.

... но похоже, что он сравнивает объект Worker с самим собой, так что, конечно, это тот же идентификатор ????

Возможно, это связано с моим java-кодом в методах назначения / отмены назначения, но я не понимаю, откуда эта проблема, у кого-нибудь есть идеи?

Изменить: Итак, после большой помощи от @ n1ck я дошел до изменения метода в классе @PlanningSolution, который возвращает список Workers для создания новых экземпляров каждый раз, но с теми же значениями. Это изменяет ошибку следующим образом: «... Рабочие объекты (Worker @ 1eba2b4d, Worker @ 67a7ef08) имеют одинаковый идентификатор планирования ...» Таким образом, он больше не сравнивает идентичные экземпляры, но, похоже, сравнивая его с экземпляром из предыдущего вызова. Однако это, по крайней мере, подтверждает, что в возвращаемом списке нет дублирования. Затем я изменил идентификатор планирования на новое (длинное) поле, значение которого всегда увеличивается с последнего созданного экземпляра, то есть идентификаторы рабочих процессов первого вызова равны 1-10, следующего вызова - 11-20 и т. Д. Это полностью обходит ошибку, но теперь ничего не выделяется для Workers. Он выполняет начальную настройку и присваивает значение null, а затем останавливается до тех пор, пока не будет достигнут предел времени на решателе. Я все еще думаю, что это то, что я не установил правильно, но ошибка, возможно, немного отвлекает, однако, как я запрошен, полная трассировка стека, как я вижу, это:

Исключение в потоке «Thread-10» java.lang.IllegalStateException: рабочие объекты (Worker @ 42094cc1, Worker @ 647a1c5c) имеют одинаковый идентификатор планирования ((class Worker, 50)). Рабочие объекты должны быть уникальными. в org.optaplanner.core.impl.domain.lookup.PlanningIdLookUpStrategy.addWorkingObject (PlanningIdLookUpStrategy.java:40) на org.optaplanner.core.impl.domain.lookup.LookUpManager.addWorknerUgobject.java: .core.impl.domain.lookup.LookUpManager.resetWorkingObjects (LookUpManager.java:43) по адресу org.optaplanner.core.impl.score.director.AbstractScoreDirector.setWorkingSolution (AbstractScoreDirector.java:167) в org.optaplanner .score.director.incremental.IncrementalScoreDirector.setWorkingSolution (IncrementalScoreDirector.java:62) в org.optaplanner.core.impl.solver.scope.DefaultSolverScope.setWorkingSolutionFromBestSolution (DefaultSolverScope.java:230) в orpl .AbstractSolver.solvingStarted (AbstractSolver.java:75) в org.optaplanner.core.impl.solver.DefaultSolver.solvingStarted (DefaultSolver.java:210) в org.optaplanner.core.impl.solver.DefaultSolver.solve. : 190) в SolverThre ad.run (SolverThread.java:86)

Изменить: класс @PlanningSolution по запросу:

package code.test.scheduler;

import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty;
import org.optaplanner.core.api.domain.solution.PlanningScore;
import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.api.domain.solution.drools.ProblemFactCollectionProperty;
import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider;
import org.optaplanner.core.api.score.buildin.hardmediumsoft.HardMediumSoftScore;
import org.optaplanner.persistence.xstream.api.score.buildin.hardmediumsoft.HardMediumSoftScoreXStreamConverter;
import com.thoughtworks.xstream.annotations.XStreamConverter;

@PlanningSolution
public class Planner
{       
@ProblemFactCollectionProperty
private java.util.List<Worker> _workerList;
@ValueRangeProvider(id="workerRange")
@ProblemFactCollectionProperty
public java.util.List<Worker> getWorkerList() {
    return _workerList;
}
public void setWorkerList(java.util.List<Worker> newWorkerList) {
    _workerList = newWorkerList;
}
public int getWorkerListSize() {
    return _workerList.size();
}

@ProblemFactCollectionProperty
private int[][] _travelTimes;
@ValueRangeProvider(id="travelTimes")
@ProblemFactCollectionProperty
public int[][] getTravelTimes() {
    return _travelTimes;
}
public void setTravelTimes(int[][] newTravelTimes) {
    _travelTimes = newTravelTimes;
}

@PlanningEntityCollectionProperty
private java.util.List<Job> _jobList;
public java.util.List<Job> getJobList() {
    return _jobList;        
}
public void setJobList(java.util.List<Job> newJobList) {
    _jobList = newJobList;
}

@XStreamConverter(HardMediumSoftScoreXStreamConverter.class)
private HardMediumSoftScore _score;

@PlanningScore
public HardMediumSoftScore getScore() {
    return _score;
}

public void setScore(HardMediumSoftScore score) {
    _score = score;     
}
}

person Matt    schedule 30.07.2018    source источник
comment
Первое исключение - это нормально, это необходимо для многопоточного решения. Я проверю, ясно ли это указано в документации. Второй более интересен: workingObjects (Worker@4438b624, Worker@4438b624) да, это дважды один и тот же экземпляр (при условии, что вы не перезаписывали ObjecttoString()). Какова полная трассировка стека этого второго исключения?   -  person Geoffrey De Smet    schedule 02.08.2018


Ответы (1)


Думаю, в вашей коллекции PlanningEntityCollection есть несколько повторяющихся сущностей. Вы уверены, что все объекты Worker в этом списке уникальны (например, не один и тот же экземпляр)?

Я специально продублировал объект в своей коллекции объектов и получил ту же ошибку.

Изменить: оказалось, что проблема заключалась в объявлении аннотации @ProblemFactCollectionProperty над полем и получателем. Его следует только объявлять выше того или другого. Объявление его над полем и геттером в сочетании с использованием аннотации @PlanningId приведет к ошибке «Рабочие объекты должны быть уникальными».

person n1ck    schedule 31.07.2018
comment
Я верю, что нет, но, если я нахожусь в тупике, как вы создали это намеренно? Возможно, это приведет меня к моей ошибке. Я попытался выполнить двойную проверку с небольшим набором данных о проблеме и не увидел дублирования, но, возможно, вы знаете способ создать эту проблему в середине решения? Он работал со старым optaplanner, но я стараюсь никогда не недооценивать свою несостоятельность. - person Matt; 31.07.2018
comment
Я просто взял первую сущность из моей коллекции сущностей и добавил ее снова. Так что я не создавал проблему в середине решения. Единственный способ воспроизвести это в середине решения - это изменить факт проблемы. Я предполагаю, что вы получаете эту ошибку сразу, а не через какое-то время, верно? Причина, по которой все работало со старой версией Optaplanner, вероятно, заключается в том, что вы раньше не использовали аннотацию @PlanningId, возможно, вы могли бы попробовать использовать ее с 7.7. - person n1ck; 31.07.2018
comment
Я попробую 7.7 с аннотацией, поскольку вы правы, но я вообще использую только аннотацию, потому что это подсказывает первое исключение. Есть ли альтернативное решение этой ошибки? - person Matt; 31.07.2018
comment
Глядя на исходный код Optaplanner, возможно, сработает установка LookUpStrategy через аннотацию @PlanningSolution. Вы можете изменить его на EQUALITY, но это потребует от вас реализации равенства и хэш-кода в объектах и ​​переменных. Пример: @PlanningSolution(lookUpStrategyType = LookUpStrategyType.EQUALITY). Однако нет причин, по которым @PlanningId не должен работать. - person n1ck; 31.07.2018
comment
Спасибо за это. Это еще кое-что, чтобы попробовать. Я немного отвлекся на дальнейшее изучение трассировки стека ошибки. Это происходит после resetWorkingObjects, но мне интересно, если рабочие планируют факты и, следовательно, не сбрасывают (или не должны этого делать), он просто добавляет их снова? Я также проверил решение при запуске без аннотации, и после определения решения дублирования тоже нет. - person Matt; 31.07.2018
comment
Похоже, что код, на который вы ссылаетесь, добавляет рабочие объекты в новую пустую HashMap. Я могу дать вам последнее предложение, не глядя на ваш код. Я нашел это в документации, однако не знаю, так ли это для вас: Может быть несколько аннотированных членов @ProblemFactCollectionProperty. Они могут даже возвращать коллекцию с тем же типом класса, но они не должны возвращать один и тот же экземпляр дважды. Вместо Collection он также может возвращать массив. - person n1ck; 31.07.2018
comment
Это то, что я сделал неправильно. Хотя обычно это не имеет значения, это вызвало ошибку. Так что он буквально БЫЛ сравнивал что-то само с собой. Однако теперь у меня есть дублирование рабочих в новом списке с новыми идентификаторами каждый раз, когда он никогда не пытается назначить работу кому-либо из них. - person Matt; 31.07.2018
comment
В любом случае размещение одного и того же экземпляра в списке дважды - это плохая практика, поскольку это искажает вероятность выбора объекта. Но с функциональной точки зрения PlanningId действительно обнаруживает это и быстро терпит неудачу. Я улучшу сообщение об ошибке. - person Geoffrey De Smet; 02.08.2018
comment
@Matt другая проблема, новый вопрос о stackoverflow, пожалуйста :) - person Geoffrey De Smet; 02.08.2018
comment
@GeoffreyDeSmet В любом одном вызове для получения списка нет повторяющихся экземпляров, но похоже, что optaplanner объединяет вызовы для создания дублирования. См. Отредактированный вопрос для прогресса. Не уверен, что понимаю, в чем должен быть новый вопрос, извините. - person Matt; 02.08.2018
comment
@ Мэтт Я думаю, вы неправильно поняли мой последний комментарий. Если у вас есть несколько списков, содержащих факты проблемы (т.е. несколько @ProblemFactCollectionProperty аннотаций), то эти списки должны содержать уникальные факты (чтобы ни один факт не отображался в обоих списках). Если у вас есть только один список, мое предыдущее предложение не применимо (это был долгий шанс). Вы, конечно, не должны возвращать разные экземпляры в одном списке или разные идентификаторы для одного и того же факта, потому что это действительно приведет к тому, что Optaplanner не будет назначать какие-либо объекты. Было бы очень полезно, если бы вы показали свой класс решения, если возможно конечно :) - person n1ck; 02.08.2018
comment
@ n1ck Приношу свои извинения. Тем не менее, я думаю, что кое-чему научился, вмешиваясь в это. Надеюсь, теперь вопрос включает в себя то, что вы хотели увидеть. Спасибо за ваш вклад. - person Matt; 02.08.2018
comment
@Matt хорошо, это помогло, потому что я думаю, что заметил это: вы указали @ProblemFactCollectionProperty над своими полями и геттерами. Это приведет к ошибке «рабочие объекты должны быть уникальными». Я бы предложил удалить аннотацию из полей (как workerList, так и travelTimes). Пожалуйста, подтвердите, чтобы я мог обновить ответ - person n1ck; 02.08.2018
comment
@ n1ck Я мог бы тебя поцеловать! Обновите свой ответ, пожалуйста. :) - person Matt; 02.08.2018