# Why don't remotecalls to a WorkerPool block when all workers are busy?

**URL:** https://discourse.julialang.org/t/why-dont-remotecalls-to-a-workerpool-block-when-all-workers-are-busy/22059
**Category:** Julia at Scale
**Tags:** parallel
**Created:** [March 19, 2019, 4:18pm UTC](https://discourse.julialang.org/t/why-dont-remotecalls-to-a-workerpool-block-when-all-workers-are-busy/22059 "2019-03-19T16:18:43Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![Cody-G](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cody-g/32/7502_2.png) [@Cody-G](https://discourse.julialang.org/u/Cody-G)
#### Post date: [March 19, 2019, 4:18pm UTC](https://discourse.julialang.org/t/why-dont-remotecalls-to-a-workerpool-block-when-all-workers-are-busy/22059/1 "2019-03-19T16:18:43Z")

</div>

Based on the docs for `remotecall` I expect the WorkerPool variant to block until a worker in the pool is available for a new job. From docs:

```julia
  remotecall(f, pool::AbstractWorkerPool, args...; kwargs...) -> Future

  WorkerPool variant of remotecall(f, pid, ....). Wait for and take a free worker from pool and perform a remotecall on it.

```

So I’m confused as to why when I run the script below 10 “started” messages are printed, and then a few seconds later all the “finished” messages are printed.

```julia
using Distributed

nwork = 2
addprocs(max(0, nwork-nworkers()))
njobs = 10

ws = workers()
wp = CachingPool(ws)
rrefs = Vector{Any}(undef, njobs)

@everywhere function rfunc(i::Int)
    print("starting #$i\n")
    sleep(3)
    print("finished #$i\n")
    return i
end

for i = 1:njobs
    rrefs[i] = remotecall(rfunc, wp, i)
end

```

Here’s the output:

```julia
julia> include("demo_pool.jl")

julia> From worker 2:	starting #1
      From worker 2:	starting #3
      From worker 2:	starting #5
      From worker 2:	starting #7
      From worker 2:	starting #9
      From worker 3:	starting #2
      From worker 3:	starting #4
      From worker 3:	starting #6
      From worker 3:	starting #8
      From worker 3:	starting #10
      From worker 2:	finished #1
      From worker 2:	finished #3
      From worker 2:	finished #5
      From worker 2:	finished #7
      From worker 2:	finished #9
      From worker 3:	finished #2
      From worker 3:	finished #4
      From worker 3:	finished #6
      From worker 3:	finished #8
      From worker 3:	finished #10

```

If I do an `@async` `remotecall_fetch` it behaves like I intended, but I didn’t think that should be necessary. Here’s the output in that case:

```julia
julia> include("demo_pool.jl")

julia> From worker 3:	starting #2
      From worker 2:	starting #1
      From worker 2:	finished #1
      From worker 3:	finished #2
      From worker 3:	starting #3
      From worker 2:	starting #4
      From worker 3:	finished #3
      From worker 2:	finished #4
      From worker 3:	starting #5
      From worker 2:	starting #6
      From worker 3:	finished #5
      From worker 2:	finished #6
      From worker 3:	starting #7
      From worker 2:	starting #8
      From worker 3:	finished #7
      From worker 2:	finished #8
      From worker 3:	starting #9
      From worker 2:	starting #10
      From worker 3:	finished #9
      From worker 2:	finished #10

```

---

<div class="post-metadata">

### Author: ![vancleve](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vancleve/32/4183_2.png) [@vancleve](https://discourse.julialang.org/u/vancleve)
#### Post date: [March 19, 2019, 8:36pm UTC](https://discourse.julialang.org/t/why-dont-remotecalls-to-a-workerpool-block-when-all-workers-are-busy/22059/2 "2019-03-19T20:36:08Z")

</div>

I had the same problem with [`remote_do`](https://docs.julialang.org/en/v1/stdlib/Distributed/index.html#Distributed.remote_do-Tuple%7BAny,AbstractWorkerPool,Vararg%7BAny,N%7D%20where%20N%7D)

---

<div class="post-metadata">

### Author: ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)
#### Post date: [March 20, 2019, 6:59am UTC](https://discourse.julialang.org/t/why-dont-remotecalls-to-a-workerpool-block-when-all-workers-are-busy/22059/3 "2019-03-20T06:59:09Z")

</div>

The worker is free during sleep. If it would be a long calculation taking time, things would look different. I think sleep yields to other tasks.

---

<div class="post-metadata">

### Author: ![Cody-G](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cody-g/32/7502_2.png) [@Cody-G](https://discourse.julialang.org/u/Cody-G)
#### Post date: [March 20, 2019, 1:29pm UTC](https://discourse.julialang.org/t/why-dont-remotecalls-to-a-workerpool-block-when-all-workers-are-busy/22059/4 "2019-03-20T13:29:22Z")

</div>

That still wouldn’t explain why 10 tasks are apparently running simultaneously and yielding to one another when the size of the WorkerPool is only 2. Based on the docs I would have expected `remotecall` to block the main thread until the pool has available workers.

Also note that the behavior doesn’t change when `sleep` is replaced with computation. If you replace the sleep statement with this loop you’ll see what I mean.

```julia
    s = 0
    for i in 1:10^9
        s+=rand()
    end

```

(I understand that at the OS level the process may yield even in the middle of this loop, but regardless I’m still puzzled as to why Julia starts so many jobs in the first place.)

---

<div class="post-metadata">

### Author: ![hsgg](https://avatars.discourse-cdn.com/v4/letter/h/838e76/32.png) [@hsgg](https://discourse.julialang.org/u/hsgg)
#### Post date: [June 27, 2019, 8:42pm UTC](https://discourse.julialang.org/t/why-dont-remotecalls-to-a-workerpool-block-when-all-workers-are-busy/22059/5 "2019-06-27T20:42:41Z")

</div>

I’d also be interested in what is going on here. In my case I run out of memory when all jobs are queued without blocking.

Thanks for the `@async remotecall_fetch()` idea. It’s not an intuitive construction!

---

<div class="post-metadata">

### Author: ![vancleve](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vancleve/32/4183_2.png) [@vancleve](https://discourse.julialang.org/u/vancleve)
#### Post date: [August 31, 2021, 9:45pm UTC](https://discourse.julialang.org/t/why-dont-remotecalls-to-a-workerpool-block-when-all-workers-are-busy/22059/6 "2021-08-31T21:45:58Z")

</div>

If anyone has any insight, I’m still curious about the behavior of `remotecall` with a `WorkerPool` here and why it doesn’t block. Are the docs just incorrect? Or are we missing something about why @Cody-G needed a `@async remotecall_fetch` to get the process to block correctly and only have `nwork` functions running simultaneously?

---

<div class="post-metadata">

### Author: ![vancleve](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vancleve/32/4183_2.png) [@vancleve](https://discourse.julialang.org/u/vancleve)
#### Post date: [September 3, 2021, 7:09pm UTC](https://discourse.julialang.org/t/why-dont-remotecalls-to-a-workerpool-block-when-all-workers-are-busy/22059/7 "2021-09-03T19:09:20Z")

</div>

I can answer this question now having dived a bit into the `Distributed.jl` code.

The `WorkerPool` version of `remotecall` does a `take!` on the `WorkerPool` to get a free worker, runs the `remotecall`, and then adds the worker back to the `WorkerPool`. Since `remotecall` just schedules `rfunc` to run on the worker asynchronously, it returns immediately and thus the worker is immediately added back to the `WorkerPool`. Thus, all the jobs are scheduled immediately on the available workers.

In contrast, `remotecall_fetch` waits for the result of `rfunc` running on the worker. So the `WorkerPool` version of `remotecall_fetch` doesn’t add the worker back to the `WorkerPool` until `rfunc` completes on the worker. Thus, only `nwork` number of jobs are run on the workers at any one time.

Considering this, it makes me think that maybe a `WorkerPool` version of `remote_call` isn’t that useful. After all, it just pulls a worker from the pool and immediately puts it back. In the case above, you end up with an equal number of tasks on each worker running simultaneously, but there is no guarantee of that. Anyway, hope this helps!
