Can anyone explain the right way to handle this? I have some code that runs in a Task
, which calls out to a library function. That function internally makes use of a GenServer
, and in some circumstances the server terminates. I’d like my calling code to detect this failure, so I can update the task status, but so far I haven’t figured out how to stop the task terminating along with the server it calls (even though as far as I can tell they’re not linked). It seems like I need to trap exits somewhere, but I haven’t been able to figure out where or how.
Here’s an extremely simplified application that demonstrates the behaviour:
# the genserver that crashes – pretend this is in an external dependency
defmodule Crash.Worker do
use GenServer
def start_link(arg), do: GenServer.start_link(__MODULE__, arg, name: __MODULE__)
def init(_), do: {:ok, nil}
def crash, do: GenServer.call(__MODULE__, :unhandled)
end
# calling code
defmodule Crash do
def run do
IO.puts("Calling crashing genserver")
Crash.Worker.crash()
IO.puts("Done")
end
end
Output (never gets to the Done):
crash (main)$ iex -S mix
Erlang/OTP 25 [erts-13.0.4] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [jit:ns]
Interactive Elixir (1.14.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Crash.run
Calling crashing genserver
14:52:34.065 [error] GenServer Crash.Worker terminating
** (RuntimeError) attempted to call GenServer Crash.Worker but no handle_call/3 clause was provided
(crash 0.1.0) lib/gen_server.ex:787: Crash.Worker.handle_call/3
(stdlib 4.0.1) gen_server.erl:1146: :gen_server.try_handle_call/4
(stdlib 4.0.1) gen_server.erl:1175: :gen_server.handle_msg/6
(stdlib 4.0.1) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Last message (from #PID<0.154.0>): :unhandled
State: nil
Client #PID<0.154.0> is alive
(stdlib 4.0.1) gen.erl:256: :gen.do_call/4
(elixir 1.14.0) lib/gen_server.ex:1035: GenServer.call/3
(crash 0.1.0) lib/crash.ex:10: Crash.run/0
(stdlib 4.0.1) erl_eval.erl:744: :erl_eval.do_apply/7
(elixir 1.14.0) src/elixir.erl:288: :elixir.eval_forms/3
(elixir 1.14.0) lib/module/parallel_checker.ex:100: Module.ParallelChecker.verify/1
(iex 1.14.0) lib/iex/evaluator.ex:329: IEx.Evaluator.eval_and_inspect/3
(iex 1.14.0) lib/iex/evaluator.ex:303: IEx.Evaluator.eval_and_inspect_parsed/3
** (exit) exited in: GenServer.call(Crash.Worker, :unhandled, 5000)
** (EXIT) an exception was raised:
** (RuntimeError) attempted to call GenServer Crash.Worker but no handle_call/3 clause was provided
(crash 0.1.0) lib/gen_server.ex:787: Crash.Worker.handle_call/3
(stdlib 4.0.1) gen_server.erl:1146: :gen_server.try_handle_call/4
(stdlib 4.0.1) gen_server.erl:1175: :gen_server.handle_msg/6
(stdlib 4.0.1) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
(elixir 1.14.0) lib/gen_server.ex:1038: GenServer.call/3
(crash 0.1.0) lib/crash.ex:10: Crash.run/0