kaa.python

kaa.python

Task.await terminates GenServer because of timeout. How to fix?

My Genserver terminates after a little while, after sending a few http requests. I can’t understand the reason:

[error] GenServer MyGenServer terminating
** (stop) exited in: Task.await(%Task{owner: #PID<0.420.0>, pid: #PID<0.1054.0>, ref: #Reference<....>}, 5000)
    ** (EXIT) time out
    (elixir) lib/task.ex:416: Task.await/2
    (elixir) lib/enum.ex:966: Enum.flat_map_list/2
    (my_app123) lib/my_genserver.ex:260: MyApp.MyGenServer.do_work/1
    (my_app123) lib/my_genserver.ex:180: MyApp.MyGenServer.handle_info/2
    (stdlib) gen_server.erl:601: :gen_server.try_dispatch/4
    (stdlib) gen_server.erl:683: :gen_server.handle_msg/5
    (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Last message: :tick
State: [%{var1: "fdsafdsfd", var2: "43243242"}]

A chunk of the code:

  # it's called from handle_info

  def do_work(some_data) do
    Enum.map(some_data, fn(x) ->
      Task.async(fn ->
        case HTTPoison.post(.....) do
        # ...........

Is “Task.async” causing the timeout? But why? Yes, it can take more than 5 seconds to complete, but why does it cause an exception which then terminates GenServer? How to fix it?

About await:

If the timeout is exceeded, await will exit; however, the task will continue to run. When the calling process exits, its exit signal will terminate the task if it is not trapping exits.

Most Liked

peerreynders

peerreynders

Actually exit/1 can be caught:

iex(1)> try do
...(1)>   exit("foo")
...(1)> catch 
...(1)>   :exit, _ -> :caught
...(1)> end
:caught

however your objection is still intact:

  • the timeout doesn’t have anything to do with the Task, which just keeps running - it is the await/2 process that times out
  • the termination of the await/2 process emits an exit signal to all linked processes. That exit signal cannot be “caught” - the exit signal will terminate any linked processes which aren’t trapping exits and those which do trap exits will get an :EXIT message in their mailbox.

.

  1. Kernel.exit/1 can only be caught inside the process that invokes it - if it is allowed to escape the process it turns into an exit signal - at which point it is too late to catch it anywhere.

  2. Process.exit/2 is an entirely different animal. While exit/1 terminates the process that invokes it, exit/2 sends an exit signal to the specified process usually with the intent to “tell that process to terminate”. So exit/2 cannot be caught under any circumstances (but it can be trapped - unless the reason is :kill).

.

iex(2)> try do
...(2)>   Process.exit(self(),"foo")
...(2)> catch                       
...(2)>   :exit, _ -> :caught       
...(2)> end                         
** (EXIT from #PID<0.87.0>) "foo"

.

iex(1)> Process.flag :trap_exit, true 
false
iex(2)> try do                       
...(2)>   Process.exit(self(),"foo") 
...(2)> catch                        
...(2)>   :exit, _ -> :caught        
...(2)> end                          
true
iex(3)> flush()
{:EXIT, #PID<0.103.0>, "foo"}
:ok
NobbZ

NobbZ

Task.await/1 calls exit/1 on timeout, so it will end your current process. If you do not wan’t that behaviour you will have to implement the receiving of the result and receiving of :DOWN messages on your own.

benperiton

benperiton

I’m still getting to grips with Tasks myself, but I think it’s because the Task is linked to the calling process, so if it throws an error and exits, then the calling process will also die. See Task — Elixir v1.20.2

You could use Task.start_link/1 if you don’t need the response, I’ve been using a Task.Supervisor for mine, so that if a Task dies, it doesn’t bring the genserver down.

Add a supervisor:

supervisor(Task.Supervisor, [[name: App.MyTaskSupervisor]])

Then can use it like:

def do_work(some_data) do
  Enum.map(some_data, fn(x) ->
    Task.Supervisor.async_nolink(App.MyTaskSupervisor, fn ->
      case HTTPoison.post(.....) do

Because I wnted to know if it was a timeout, I use yield

task = Task.Supervisor.async_nolink(
  App.MyTaskSupervisor,
  MyModule,
  :task_function,
  [args]
)

case Task.yield(task) || Task.shutdown(task) do
  {:ok, result} ->
    ack_message({:ok, %{channel: channel, tag: tag}})
  
  {:error, msg} ->
    ack_message({:error, %{error: msg, channel: channel, tag: tag, redelivered: redelivered}})

  {:exit, reason} ->
    ack_message({:error, %{error: reason, channel: channel, tag: tag, redelivered: redelivered}})

  nil ->
    ack_message({:error, %{error: "TIMEOUT", channel: channel, tag: tag, redelivered: redelivered}})
end

Where Next?

Popular in Questions Top

sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
tduccuong
Hi, is there any work on GUI with Elixir, that is similar to Electron/Javascript? My idea is to bundle Phoenix and BEAM into a single se...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

Other popular topics Top

siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 39467 209
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement