I observe that using HTTP.request within an interactive thread makes the request run, in part, on a default thread. The result is that when the default thread(s) are busy, the request is delayed (i.e. not "interactive"). Below is an example where we keep a thread in the default thread-pool busy by computing an approximation to pi (via the compute method), and we use a thread in the interactive thread-pool to make an http request once every two seconds (via the interact method). We can exercise this pattern using HTTP.jl or LibCURL.jl for the http request. In the case of HTTP.jl the interact method is blocked until the compute method finishes where-as in the case of LibCurl.jl, the interact and compute methods run simultaneously, as expected.
using HTTP,LibCURL
function compute(tic)
@info "compute" Threads.threadpool() Threads.threadid()
x = 1
c = -1
d = 3
while true
if time() - tic > 30
break
end
x += c/d
c *= -1
d += 2
end
@info "approximation to pi is $(4*x)"
end
function interact(tic, ishttp)
@info "interact" Threads.threadpool() Threads.threadid()
while true
if ishttp
r = HTTP.request("GET", "https://example.com")
@info "status=$(r.status)"
else
curl = curl_easy_init()
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com")
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0)
redirect_stdout(devnull) do
curl_easy_perform(curl)
end
status = Clong[300]
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status)
@info "status=$(status[1])"
curl_easy_cleanup(curl)
end
sleep(2)
if time() - tic > 32
break
end
end
nothing
end
ishttp = parse(Bool, ARGS[1])
tic = time()
t1 = Threads.@spawn :interactive interact(tic, ishttp)
sleep(.001) # not sure why this is needed, but without it, the interactive thread sometimes does not run.
t2 = Threads.@spawn :default compute(tic)
wait.((t1,t2))
Here is the result using ishttp=true:
$ julia -t 1,1 --project=@http httptrouble.jl true
┌ Info: interact
│ Threads.threadpool() = :interactive
└ Threads.threadid() = 1
┌ Info: compute
│ Threads.threadpool() = :default
└ Threads.threadid() = 2
[ Info: approximation to pi is 3.1415926553757925
[ Info: status=200
For what it is worth, the real-world use case here is monitoring for spot eviction events when using Azure cloud virtual machines.
I suppose the solution is to somehow propagate the interactive thread-pool usage through the HTTP.jl layers so that the appropriate thread pool can be used when Threads.@spawn is called, but I'm not super familiar with the structure of the layers in HTTP.jl, and I'm not sure what the best mechanism for propagating the interactive thread usage is. But, perhaps the simplest solution is to use something like Threads.@spawn Threads.threadpool() ... in places like this.
I observe that using
HTTP.request
within an interactive thread makes the request run, in part, on a default thread. The result is that when the default thread(s) are busy, the request is delayed (i.e. not "interactive"). Below is an example where we keep a thread in the default thread-pool busy by computing an approximation to pi (via thecompute
method), and we use a thread in the interactive thread-pool to make an http request once every two seconds (via theinteract
method). We can exercise this pattern using HTTP.jl or LibCURL.jl for the http request. In the case of HTTP.jl theinteract
method is blocked until thecompute
method finishes where-as in the case of LibCurl.jl, theinteract
andcompute
methods run simultaneously, as expected.Here is the result using
ishttp=true
:Here is the result using
ishttp=false
:For what it is worth, the real-world use case here is monitoring for spot eviction events when using Azure cloud virtual machines.
I suppose the solution is to somehow propagate the interactive thread-pool usage through the HTTP.jl layers so that the appropriate thread pool can be used when
Threads.@spawn
is called, but I'm not super familiar with the structure of the layers in HTTP.jl, and I'm not sure what the best mechanism for propagating the interactive thread usage is. But, perhaps the simplest solution is to use something likeThreads.@spawn Threads.threadpool() ...
in places like this.