Estou usando Timefold 1.15 e Quarkus 3.1
Estou tentando salvar os dados durante a resolução do horário
mas tenho alguns contextos que são necessários durante a resolução, contexto de locatário para segurança de linha e contexto de identidade
mas estou enfrentando problemas relacionados à indisponibilidade dos contextos
Código de exemplo:
solverManager.solveBuilder()
.withProblemId(finalRequestId)
.withProblemFinder((id) -> timeTableJob.get(finalRequestId).timetable)
.withBestSolutionConsumer(bestSolution -> timeTableJob.put(finalRequestId, Job.ofTimetable(bestSolution)))
.withFinalBestSolutionConsumer(solution -> {
timeTableJob.put(finalRequestId,Job.ofTimetable(solution));
plannedLessonRepository.save(solution);
})
.withExceptionHandler((id, exception) -> {
timeTableJob.put(finalRequestId, Job.ofException(exception));
LOGGER.error("Failed solving jobId ({}).", finalRequestId, exception);
})
.run();
Erro:
2024-11-26 17:18:32,463 ERRO [org.acm.sch.res.TimeTableResource] (pool-13-thread-1) Falha ao resolver jobId (101).: org.hibernate.HibernateException: SessionFactory configurado para multilocação, mas nenhum identificador de locatário especificado em org.hibernate.internal.AbstractSharedSessionContract.getTenantId(AbstractSharedSessionContract.java:292) em org.hibernate.internal.AbstractSharedSessionContract.(AbstractSharedSessionContract.java:193) em org.hibernate.internal.SessionImpl.(SessionImpl.java:230) em org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl.openSession(SessionFactoryImpl.java:1381) em org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl.openSession(SessionFactoryImpl.java:1247) em io.quarkus.hibernate.orm.runtime.session.JTASessionOpener.openSession(JTASessionOpener.java:46) em io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.acquireSession(TransactionScopedSession.java:92) em io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.find(TransactionScopedSession.java:175) em org.hibernate.engine.spi.SessionLazyDelegator.find(SessionLazyDelegator.java:825) em org.hibernate.Session_OpdLahisOZ9nWRPXMsEFQmQU03A_Synthetic_ClientProxy.find(Fonte desconhecida) em io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations.findById(AbstractJpaOperations.java:183) em org.acme.timetable.persistence.PlannedLessonRepository.findById(PlannedLessonRepository.java) em org.acme.timetable.persistence.PlannedLessonRepository.findById(PlannedLessonRepository.java) em org.acme.timetable.persistence.PlannedLessonRepository.save(PlannedLessonRepository.java:24) em org.acme.timetable.persistence.PlannedLessonRepository_Subclass.save$$superforward(Fonte desconhecida) em org.acme.timetable.persistence.PlannedLessonRepository_Subclass$$function$$1.apply(Fonte desconhecida) em io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:73) em io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:62) em io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:136) em io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:107) em io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired.doIntercept(TransactionalInterceptorRequired.java:38) em io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.intercept(TransactionalInterceptorBase.java:61) em io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired.intercept(TransactionalInterceptorRequired.java:32) em io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired_Bean.intercept(Fonte desconhecida) em io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:42) em io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:30) em io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:27) em org.acme.timetable.persistence.PlannedLessonRepository_Subclass.save(Fonte desconhecida) em org.acme.timetable.persistence.PlannedLessonRepository_ClientProxy.save(Fonte desconhecida) em org.acme.timetable.rest.TimeTableResource.lambda$solve$2(TimeTableResource.java:112) em ai.timefold.solver.core.impl.solver.ConsumerSupport.lambda$consumeFinalBestSolution$1(ConsumerSupport.java:102) em java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) em java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) em java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) em java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) em java.base/java.lang.Thread.run(Thread.java:833)
O melhor consumidor de solução, o melhor consumidor de solução final e o manipulador de exceção são executados em um thread diferente daquele que aceita a chamada da API. Como resultado, o contexto da transação não é propagado.
O que você pode fazer é usar o QuarkusTransaction para iniciar e encerrar a transação no melhor consumidor de solução final (e remover a
@Transactional
anotação).