I've reduced the size of the question, cause it was too big. Here's the code:
defmodule MayRaiseGenServer do
use GenServer
def start_link do
IO.puts "started MyServer, name is #{__MODULE__}"
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def maybe_will_raise do
GenServer.call(__MODULE__, :maybe_will_raise)
end
def handle_call(:maybe_will_raise,_from, state) do
IO.puts "maybe_will_raise called!"
:random.seed(:erlang.now)
number = Enum.to_list(1..100) |> Enum.shuffle |> List.first
IO.puts "number is #{number}"
if rem(number,2) != 0 do
raise "#{number}"
end
{:reply, {"You got lucky"}, state}
end
end
defmodule MayRaiseSupervisor do
use Supervisor
def start_link([]) do
IO.puts "starting supervisor, name is #{__MODULE__}"
Supervisor.start_link(__MODULE__, [])
end
def init(arg) do
IO.puts "initted with arg: #{arg}"
children = [
worker(MayRaiseGenServer, [])
]
supervise(children, strategy: :one_for_one, restart: :transient, name: __MODULE__)
end
end
MayRaiseSupervisor.start_link([])
IO.inspect MayRaiseGenServer.maybe_will_raise
:timer.sleep(2000)
IO.puts "after sleep"
Initially, I only saw the message for starting the GenServer once, but now I see it again. Here's the output:
starting supervisor, name is Elixir.MayRaiseSupervisor
initted with arg:
started MyServer, name is Elixir.MayRaiseGenServer
maybe_will_raise called!
number is 14
started MyServer, name is Elixir.MayRaiseGenServer
11:32:28.807 [error] GenServer MayRaiseGenServer terminating
** (RuntimeError) 14
lib/mini.ex:20: MayRaiseGenServer.handle_call/3
(stdlib) gen_server.erl:615: :gen_server.try_handle_call/4
(stdlib) gen_server.erl:647: :gen_server.handle_msg/5
(stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Last message: :maybe_will_raise
State: []
** (exit) exited in: GenServer.call(MayRaiseGenServer, :maybe_will_raise, 5000)
** (EXIT) an exception was raised:
** (RuntimeError) 14
lib/mini.ex:20: MayRaiseGenServer.handle_call/3
(stdlib) gen_server.erl:615: :gen_server.try_handle_call/4
(stdlib) gen_server.erl:647: :gen_server.handle_msg/5
(stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
(elixir) lib/gen_server.ex:604: GenServer.call/3
lib/mini.ex:45: (file)
(elixir) lib/code.ex:363: Code.require_file/2
From the above output, it's not very clear to me what happens. It looks like the GenServer is restarted, based on the message shown on IO, but why is the exception being thrown again? Also, in this code:
MayRaiseSupervisor.start_link([])
IO.inspect MayRaiseGenServer.maybe_will_raise
:timer.sleep(2000)
IO.puts "after sleep"
If the method call MayRaiseGenServer.maybe_will_raise
will indeed raise an error, it looks like the lines after, the one with timer.sleep
and the IO.puts
won't be run anymore. Even if I change the code to try and handle the exception, like this:
MayRaiseSupervisor.start_link([])
try do
IO.inspect MayRaiseGenServer.maybe_will_raise
rescue
RuntimeError -> IO.puts "there was an error"
end
:timer.sleep(2000)
IO.puts "after sleep"
I still cannot seem to reach the last IO.puts
( if there was an error ). Is there a way of handling the call to maybe_will_raise
that would allow me to handle it raising an error, and continuing execution? I'm guessing supervisors won't automatically retry a piece of code when they're being restarted.
As my point of view.
Your output above is telling you a stack trace when an exception was raised with an exit signal in GenServer.call(MayRaiseGenServer, :maybe_will_raise, 5000)
and an error log because terminate/2
is invoked with a reason {%RuntimeError{message: ...}, [...]
.
You can define terminate/2
callback to see:
def terminate(reason, _state) do
IO.inspect reason
end
Terminate/2
If reason is not :normal, :shutdown nor {:shutdown, term} an error is logged.
But when an exception was raised inside a GenServer callback (except init/1
) it will invoke terminate/2
that telling the server is about to exit (Exit signal was sent).
So code after this line will not be executed:
try do
IO.inspect MayRaiseGenServer.maybe_will_raise
...
but shouldn't the IO.puts "started MyServer" output appear again?
And also when your GenServer is exited.Your supervisor will start a new one linking the main process with your GenServer process (Your MayRaiseGenServer.start_link
got call again)
The last thing is if you want to make the code continue executing.You can catch exit signal like this:
MayRaiseSupervisor.start_link([])
try do
IO.inspect MayRaiseGenServer.maybe_will_raise
catch
:exit, _ -> IO.puts "there was an error"
end
:timer.sleep(2000)
IO.puts "after sleep"
But i think you should consider of using raise
in your GenServer callback.Hope that help!
The problem is that your Supervisor is not linked to the the GenServer. Therefore it is not notified about death of the child (through an exit signal). To fix that, you need to use GenServer.start_link/3
.
For more information check either Erlang or Elixir documentation on processes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With