minhajuddin
Elixir task timeout pitfall
I have written a blog post about a common misunderstanding about tasks
Would love your inputs 
Most Liked
benwilson512
Look, I’m 100% behind having a blog post which says "hey, keep in mind that these calls will run sequentially, that’s why there’s Task.yield_many`.
I mostly just feel like the sentence “it should show you that running in parallel with timeouts is not just a Task.await away.” implies that there’s something wrong with how the Elixir Task.await function works, and that no easy solution is present. A more accurate summary would be something like:
it should show you that if you want tasks to share a timeout, you want Task.yield_many instead of consecutive Task.await
The other issue is that the blog post doesn’t actually explain WHY the behaviour is happening, and why it isn’t actually unexpected once you understand what each part does.
I don’t really agree that it’s a “common misunderstanding” or a “pitfall” either, but those are definitely more subjective areas.
benwilson512
No, you’re setting the same timeout for each task. However, each call to Task.await is consecutive. Thus, the first task is given 5 seconds to timeout. Then the next task is given 5 seconds to timeout, and so forth. Every task is given 5 seconds. This is absolutely expected because the Task.await call is happening inside a loop. It will happen for each item independently, because that’s how loops work.
If you want one timeout for the entire group you have to do something differently.
benwilson512
Look, suppose you wrote the following code:
task = Task.async(fn -> Process.sleep(:infinity) end)
Process.sleep(5_000)
Task.await(task, 5_000)
How long before it times out? 10 seconds of course. But this is obvious and expected. This is exactly what you’re doing by making the Task.await calls consecutive. It’s just that instead of sleeping in the main process you’re waiting on a different task. Task.await is blocking, this is expected.
OvermindDL1
Yeah I agree, await is blocking just like join is in lower-level languages, and you are joining one one task at a time in sequence, so it will take an aggregate of the times, so this is entirely expected as Task is emulating a lower-level fork/join.
benwilson512
In fact, this actually perfectly demonstrates parallel execution at work. The total amount of sleeping time called is 49.5 seconds!
1..10 |> Enum.map(&(&1 * 900)) |> Enum.sum
Despite this, the total wait time is only about as long as the longest item 9005.466 ms (not the 10 seconds in the article).








