diff --git a/web/backend/Dockerfile b/web/backend/Dockerfile index b85e63f..7003ede 100644 --- a/web/backend/Dockerfile +++ b/web/backend/Dockerfile @@ -6,7 +6,7 @@ WORKDIR /app COPY Project.toml /app/Backend/ COPY src /app/Backend/src RUN julia --project=. -e 'using Pkg; Pkg.develop(path="Backend"); Pkg.add("HiGHS"); Pkg.add("JuMP"); Pkg.precompile()' -COPY docker/startup.jl ./ +COPY startup.jl ./ # Set timezone to Chicago ENV TZ=America/Chicago @@ -17,4 +17,4 @@ ENV UCJL_HOST="0.0.0.0" ENV UCJL_PORT="9000" # Run the server -CMD ["julia", "--threads", "1", "--procs", "1", "--project=.", "startup.jl"] +CMD ["julia", "--threads", "1", "--procs", "4", "--project=.", "startup.jl"] diff --git a/web/backend/Project.toml b/web/backend/Project.toml index 46cacf7..b846ba0 100644 --- a/web/backend/Project.toml +++ b/web/backend/Project.toml @@ -8,7 +8,9 @@ CodecZlib = "944b1d66-785c-5afd-91f1-9de20f533193" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" +HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +JuMP = "4076af6c-e467-56ae-b986-b466b2749572" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" @@ -19,7 +21,9 @@ CodecZlib = "0.7.8" Dates = "1.11.0" Distributed = "1.11.0" HTTP = "1.10.19" +HiGHS = "1.20.1" JSON = "0.21.4" +JuMP = "1.29.2" Logging = "1.11.0" Printf = "1.11.0" Random = "1.11.0" diff --git a/web/backend/src/jobs.jl b/web/backend/src/jobs.jl index 3be9154..afbf64a 100644 --- a/web/backend/src/jobs.jl +++ b/web/backend/src/jobs.jl @@ -9,8 +9,8 @@ Base.@kwdef mutable struct JobProcessor pending = RemoteChannel(() -> Channel{String}(Inf)) processing = RemoteChannel(() -> Channel{String}(Inf)) shutdown = RemoteChannel(() -> Channel{Bool}(1)) - worker_pid = nothing - monitor_task = nothing + worker_pids = [] + worker_tasks = [] work_fn = nothing end @@ -56,26 +56,40 @@ function worker_loop(pending, processing, shutdown, work_fn) end function start(processor::JobProcessor) - processor.monitor_task = @spawn begin - worker_loop( - processor.pending, - processor.processing, - processor.shutdown, - processor.work_fn, - ) + # Get list of available worker processes + worker_pids = workers() + @info "Starting job processor with $(length(worker_pids)) worker(s)" + + # Start a worker loop on each worker process + for pid in worker_pids + task = @spawnat pid begin + worker_loop( + processor.pending, + processor.processing, + processor.shutdown, + processor.work_fn, + ) + end + push!(processor.worker_pids, pid) + push!(processor.worker_tasks, task) end return end function stop(processor::JobProcessor) + # Send shutdown signal (all workers will see it) put!(processor.shutdown, true) - if processor.monitor_task !== nothing + + # Wait for all worker tasks to complete + for (i, task) in enumerate(processor.worker_tasks) try - wait(processor.monitor_task) + wait(task) + @info "Worker $(processor.worker_pids[i]) stopped" catch e - @warn "Error waiting for worker task" exception=e + @warn "Error waiting for worker $(processor.worker_pids[i])" exception=e end end + return end diff --git a/web/backend/docker/startup.jl b/web/backend/startup.jl similarity index 75% rename from web/backend/docker/startup.jl rename to web/backend/startup.jl index ecbc68a..9b8c3b5 100644 --- a/web/backend/docker/startup.jl +++ b/web/backend/startup.jl @@ -1,10 +1,7 @@ -#!/usr/bin/env julia - # UnitCommitment.jl: Optimization Package for Security-Constrained Unit Commitment # Copyright (C) 2025, UChicago Argonne, LLC. All rights reserved. # Released under the modified BSD license. See COPYING.md for more details. -# Load required packages using HiGHS using JuMP using Backend @@ -15,10 +12,16 @@ const UCJL_PORT = parse(Int, get(ENV, "PORT", "9000")) println("Starting UnitCommitment Backend Server...") println("Host: $UCJL_HOST") println("Port: $UCJL_PORT") -println("Press Ctrl+C to stop the server") Backend.setup_logger() -server = Backend.start_server(UCJL_HOST, UCJL_PORT; optimizer = optimizer_with_attributes(HiGHS.Optimizer, "mip_rel_gap" => 0.001)) +server = Backend.start_server( + UCJL_HOST, + UCJL_PORT; + optimizer = optimizer_with_attributes( + HiGHS.Optimizer, "mip_rel_gap" => 0.001, + "threads" => 1, + ) +) try wait() catch e