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
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 theawait/2process that times out - the termination of the
await/2process 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:EXITmessage in their mailbox.
.
-
Kernel.exit/1can 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 tocatchit anywhere. -
Process.exit/2is an entirely different animal. Whileexit/1terminates the process that invokes it,exit/2sends an exit signal to the specified process usually with the intent to “tell that process to terminate”. Soexit/2cannot 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
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
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
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









