Class WarehouseDispatcherService
Manages the full lifecycle of a dispatch-optimisation job: submitting new jobs, monitoring their status, terminating running jobs, and persisting the final forklift-to-transport-order assignments. Acts as the bridge between the warehouse domain (bins, forklifts, transport orders) and the constraint-solving engine.
Job lifecycle: QUEUED → SOLVING → COMPLETED / FAILED / ABORTED.
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionFetcher callback used by Timefold at the start of solving.Assembles the current warehouse snapshot that the solver will optimise.findJobEntityById(UUID jobId) Retrieves the rawDispatchJobentity by its ID.getJobStatusAndReconcile(UUID jobId) Returns the current status of a dispatch job.voidsaveFinalSolution(WarehouseSchedule solution, UUID jobId) Best-solution consumer callback invoked by Timefold when a solution is available (either the final optimal solution or an intermediate best-effort one).Submits a new asynchronous optimisation job to Timefold.voidterminateOptimizationJob(UUID jobId) Attempts to gracefully terminate a running or queued optimisation job.
-
Constructor Details
-
WarehouseDispatcherService
public WarehouseDispatcherService()
-
-
Method Details
-
getJobStatusAndReconcile
Returns the current status of a dispatch job.Intended to be extended with reconciliation logic that detects when Timefold has silently stopped solving (e.g., due to an internal error) while the job is still marked as
SOLVINGin the database, and marks it asFAILEDaccordingly.- Parameters:
jobId- The unique identifier of the dispatch job.- Returns:
- A response DTO with the current job state.
- Throws:
DispatchJobNotFoundException- if no job exists for the given ID.
-
buildCurrentState
Assembles the current warehouse snapshot that the solver will optimise.Loads all storage bins, forklifts, and pending transport orders from the database into an in-memory
WarehouseScheduleobject. This serves as the problem fact set for the Timefold constraint solver.Known issue: Loading all entities without pagination is a performance bottleneck for large datasets. A more selective fetching strategy (cursor-based pagination, lazy fields, caching) should be implemented.
- Returns:
- A fully populated
WarehouseSchedulerepresenting the current state of the warehouse.
-
submitOptimizationJob
Submits a new asynchronous optimisation job to Timefold.Creates a
DispatchJobrecord in stateQUEUED, then delegates toSolverManager.solveAndListen(java.lang.Object, Solution_, java.util.function.Consumer<? super Solution_>)with:- A problem-fetcher that transitions the job to
SOLVINGand builds the current warehouse state. - A best-solution consumer that persists the final assignments and marks the job as
COMPLETED(orFAILED).
- Returns:
- The UUID assigned to the newly created optimisation job. The caller can use this ID to
poll status (
getJobStatusAndReconcile(java.util.UUID)) or to terminate the job (terminateOptimizationJob(java.util.UUID)).
- A problem-fetcher that transitions the job to
-
terminateOptimizationJob
Attempts to gracefully terminate a running or queued optimisation job.Only jobs in state
QUEUEDorSOLVINGcan be terminated. Once terminated, the job is marked asABORTEDand any partial results are discarded (the best-solution consumer checks for this flag).- Parameters:
jobId- The unique identifier of the job to terminate.- Throws:
DispatchJobInvalidStateException- if the job is already in a terminal state (COMPLETED,FAILED, orABORTED).
-
buildCurrentProblemAndSetSolvingStatus
Fetcher callback used by Timefold at the start of solving.Transitions the job from
QUEUEDtoSOLVINGin the database and then builds the current warehouse snapshot. If building the snapshot fails, the job is marked asFAILEDand the exception is rethrown to Timefold.Note: This method is annotated
@Transactionalso that the status update and snapshot build happen within the same persistence context.- Parameters:
jobId- The unique identifier of the job being started.- Returns:
- A fully populated
WarehouseSchedulefor the solver.
-
saveFinalSolution
Best-solution consumer callback invoked by Timefold when a solution is available (either the final optimal solution or an intermediate best-effort one).Persists the solver's assignments back to the database:
- Updates forklift assignments on transport orders.
- Records assigned orders on forklifts.
If the job was
ABORTEDduring solving, the solution is silently discarded. Otherwise the job is marked asCOMPLETED(orFAILEDif persistence fails). Even infeasible solutions are saved as a best-effort fallback.- Parameters:
solution- TheWarehouseScheduleproduced by Timefold, which contains the optimised assignments.jobId- The unique identifier of the job that produced this solution.
-
findJobEntityById
Retrieves the rawDispatchJobentity by its ID.- Parameters:
jobId- The unique identifier of the dispatch job.- Returns:
- The managed
DispatchJobentity. - Throws:
DispatchJobNotFoundException- if no job exists for the given ID.
-