I have a mix project with as simple as possible a Supervisor and GenServer. When I call from iex:
EchoCmd.Supervisor.start_link([:Hello])
GenServer.call(:echoserver, :echo)
GenServer.call(:echoserver, :mumble)
GenServer.call(:echoserver, :echo)
The :mumble call raises an exception, then the GenServer is restarted and the second :echo call works ok.
If I run the code in any other way the Supervisor fails to restart the GenServer. For example, I create an escript of the project with the main module as follows:
defmodule EchoCmd.Echo do
def main(args) do
EchoCmd.Supervisor.start_link([:Hello])
GenServer.call(:echoserver, :echo)
GenServer.call(:echoserver, :mumble)
GenServer.call(:echoserver, :echo)
end
end
The :mumble call raises an exception and the escript terminates without the Supervisor restarting the GenServer.
I've not included the Supervisor and Server modules code because they work fine when called from iex, so I'm guessing they're not needed here.
Do I have a conceptual misunderstanding? Is this not possible, or am I doing something wrong?
Escript behaviour is correct. You just missing how iex shell "helps you".
What you are doing in your code is starting a linked process, and than crashing it. And since it is a linked process, when it goes down, it suppose to bring down all linked processes. There could be some "exceptions", but that what's happening to your escript process.
Both shell and prcess supervisor can handle such "I died, so should you" message. They do it by changing the way process (linked process, not the dying one) processes such messages. It allows them to receive they as normal messages (that you could receive in receive
clause if you would like to) rather than special, internal ones. To change this bahaviour they use Process.flag( :trap_exit, :true)
(elixir doc pointing to eralng's one). It allows shell to just print death of killed processes, rather that dying every time you do something bad.
So you could do same thing. Change this flag, and if you wan't pattern match in receive
on such messages. But I don't think that's what you looking for. Since your process is singleton, and supervisor does all restarting, you don't really have any reason to link to it in first place. There is no need for updates on deaths and restarts, just let supervisor worry about that. It's just like Joe Armstrong said (might be paraphrasing)
You don't need to know how to fix vending machine to use it.
So just, start
, rather than start_link
.
That said, you might consider creating link with supervisor, which also might die after too many restarts (he might be told to behave in such way). And it allows you to take him (and supervised process) when you die. Or he could be linked to your supervisor, or application supervisor, or in some other manner. It depends on your domain, and there is no bad decision, you just have to check what's working for you. It is design decision, and you either have to experiment or read more about it:
http://elixir-lang.org/getting_started/mix_otp/5.html
http://www.erlang.org/doc/design_principles/des_princ.html
http://learnyousomeerlang.com/supervisors
The problem lies not in your server and supervisor, but in the way you're calling them. If the server exits while another process is waiting for a reply to GenServer.call
, the calling process exits too, so the last call never happens. The reason for this is the process couldn't possibly continue in an invalid state if a synchronous call failed (GenServer.call
is synchronous as opposed to GenServer.cast
). If you're doing this just to test the supervisor, then you can try:
defmodule EchoCmd.Echo do
def main(args) do
EchoCmd.Supervisor.start_link([:Hello])
GenServer.cast(:echoserver, :echo)
GenServer.cast(:echoserver, :mumble)
GenServer.cast(:echoserver, :echo)
end
end
The reason it works in iex
is that iex
traps the exit and allows you to type in another line.
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