Solving JuMP model async with HTTP routing doesn't work

I’m in the process of building a web server that solves a mixed-integer program. The solve is triggered when the user hits a specific url. Because the MIP solve takes a while to complete, this causes a timeout in the HTTP request. Ideally, we’d run the solve asynchronously and send back an immediate response that the solve started. However, it seems that the @async macro simply does not work inside the router if a JuMP model is optimized inside it.

Example code:

using HTTP, Cbc, JuMP, Sockets

function async_solve(request::HTTP.Request)

    @async begin
        println("async solve started")

        model = read_from_file("model.mps")
        set_time_limit_sec(model, 10)
        set_optimizer(model, Cbc.Optimizer)
        optimize!(model)

        println("async solve complete")
    end

    return HTTP.Response(200, "Done")
end

ROUTER = HTTP.Router()
HTTP.@register(ROUTER, "/", async_solve)
HTTP.serve(ROUTER, Sockets.localhost, 8080)

Note: any large enough MIP will do the job as the .mps file - let me know if you want me to provide a copy for testing, I cannot seem to upload it here directly.

Once the server is running, go to localhost:8080 in your browser and you will see the cbc logs running in the REPL while the page is still loading. If the macro functioned correctly, we’d expect to see “Done” in the browser window long before the Cbc solve finishes

We can try replace the JuMP bits with sleep(10) to see what happens when running other code in the @async block:

using HTTP, Cbc, JuMP, Sockets

function async_solve(request::HTTP.Request)

    @async begin
        println("async solve started")

        sleep(10)

        println("async solve complete")
    end

    return HTTP.Response(200, "Done")
end

ROUTER = HTTP.Router()
HTTP.@register(ROUTER, "/", async_solve)
HTTP.serve(ROUTER, Sockets.localhost, 8080)

When a browser goes to localhost:8080, it immediately displays “Done” and the REPL shows “async solve complete” after about 10 seconds as expected.

To clarify, when solving a JuMP model inside @async without the HTTP router, it works as expected
I am running Julia v1.60, JuMP v0.21.6, HTTP v0.9.12
Not too sure if this is a JuMP or HTTP issue, please advise if more information is needed here.

1 Like

You should start process on a different thread with @spawn.

2 Likes

I don’t know the details, perhaps someone more knowledgeable can chime in, but I assume the call to Cbc in the solve is blocking.

The suggestion for spawn is to start Julia with julia -t 2 (or more threads if you want), then use something like:

using HTTP, Cbc, JuMP

function solve_model()
    model = Model(Cbc.Optimizer)
    @variable(model, x[1:10], Bin)
    @objective(model, Max, rand(10)' * x)
    @constraint(model, rand(10)' * x <= 5)
    optimize!(model)
    return objective_value(model)
end

function async_solve(request::HTTP.Request)
    Threads.@spawn begin
        println("async solve started")
        cost  = solve_model()
        println("async solve complete. Cost = $(cost)")
    end
    return HTTP.Response(200, "Done")
end

ROUTER = HTTP.Router()
HTTP.@register(ROUTER, "/", async_solve)
HTTP.serve(ROUTER, "127.0.0.1", 8080)
4 Likes

Thanks a lot! That works perfectly.

3 Likes