I have a GenServer module that might fail to initialize and if it does there is no point in restarting it - it will always fail. So I would like GenServer:start_link(...)
to return {:error, :some_reason}
in this case.
From gen_server docs
start_link(Module, Args, Options) -> Result
...
If Module:init/1 fails with Reason, the function returns {error,Reason}. If Module:init/1 returns {stop,Reason} or ignore, the process is terminated and the function returns {error,Reason} or ignore, respectively.
Module:init(Args) -> Result
...
If the initialization fails, the function is to return {stop,Reason}, where Reason is any term, or ignore.
This led me to believe that I can solve my problem like this:
iex(1)> defmodule X do
...(1)> use GenServer
...(1)>
...(1)> def init(_) do
...(1)> {:stop, :boom}
...(1)> end
...(1)> end
iex(2)> GenServer.start_link(X, [])
{:error, :boom}
** (EXIT from #PID<0.101.0>) shell process exited with reason: :boom
If the reason is :normal
then it simply returns {:error, :normal}
without crashing.
As far as I can understand start_link
links the parent with the child process even before init
has completed and when {:stop, :anything}
is returned from init
the process exits abnormally and crashes the parent process that is not able to process the returned value.
I can set the trap_exit
to true
but I don’t want to trap_exit only for this. I can probably solve this by spawning a different process, trap_exit
and start_link
from there and then send a message to the parent process with the result but this is obviously a a hack.
Is there a better solution?