I am trying to understand how GenServer
and Supervisor
work by writing a project wherein a process will maintain its previous state when it is restarted.
I have a mix (sup) project in which I have three primary processes - Supervisor, Stack, Stash . There are push and pop operations on the stack. If a pop operation is performed on the stack, the process will die and then restarted by the supervisor again. Before dying, the stack will store the empty list in the stash so that it will be picked up when the stack process is restarted.
supervisor.ex
defmodule Spvsr.More.Supervisor do
use Supervisor
def start_link(initial) do
result = {:ok, sup} = Supervisor.start_link(__MODULE__, initial, name: __MODULE__)
start_workers(sup, initial)
result
end
def start_workers(sup, initial) do
Supervisor.start_child(sup, worker(Spvsr.More.Stash, [initial]))
Supervisor.start_child(sup, worker(Spvsr.More.Stack, [Spvsr.More.Stash.get_value]))
end
def init(_) do
supervise [], strategy: :one_for_one
end
end
stash.ex
defmodule Spvsr.More.Stash do
use GenServer
require Logger
def start_link(list) do
{:ok, _pid} = GenServer.start_link(__MODULE__, list, name: __MODULE__)
end
def save_value(value) do
IO.puts("setting value #{value}")
GenServer.cast __MODULE__, {:save_value, value}
end
def get_value do
IO.puts("get value")
GenServer.call __MODULE__, :get_value
end
def handle_cast({:save_value, value}, _current_value) do
{:noreply, value}
end
def handle_call(:get_value, _from, current_value) do
{:reply, current_value, current_value}
end
def init(args) do
{:ok, args}
end
end
stack.ex
defmodule Spvsr.More.Stack do
use GenServer
def start_link(list) do
GenServer.start_link(__MODULE__, list, name: __MODULE__)
end
def init(args) do
{:ok, args}
end
def shutdown(msg \\ "No msg received") do
GenServer.cast(__MODULE__, {:push, {:bye, msg}})
end
def push(n) do
GenServer.cast(__MODULE__, {:push, n})
end
def pop do
GenServer.call(__MODULE__, :pop)
end
def get do
GenServer.call(__MODULE__, :get)
end
def handle_call(:pop, _from, [head | tail]), do: {:reply, head, tail}
def handle_call(:pop, _from, []) do
Spvsr.More.Stash.save_value [] # saving the value before the process dies
{:stop, "Stack is empty", []}
end
def handle_call(:get, _from, list), do: {:reply, list, list}
def handle_cast({:push, {:bye, msg}}, list), do: {:stop, msg, list}
def handle_cast({:push, n}, list), do: {:noreply, [n | list]}
def terminate(reason, state) do
IO.puts("I m terminating #{IO.inspect reason} #{IO.inspect state} ")
:ok
end
end
application.ex
defmodule Spvsr.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
def start(_type, _args) do
# List all child processes to be supervised
children = [
# Starts a worker by calling: Spvsr.Worker.start_link(arg)
# {Spvsr.Worker, arg},
{Spvsr.More.Supervisor, [1, 2, 3, 4]}
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Spvsr.Supervisor]
Supervisor.start_link(children, opts)
end
end
Now on iex I am running the following commands
> Spvsr.More.Stack.get # [1, 2, 3, 4]
> Spvsr.More.Stack.pop # 1
> Spvsr.More.Stack.pop # 2
> Spvsr.More.Stack.pop # 3
> Spvsr.More.Stack.pop # 4
> Spvsr.More.Stack.pop
setting value
"Stack is empty"
[]
I m terminating Stack is empty
** (exit) exited in: GenServer.call(Spvsr.More.Stack, :pop, 5000)
** (EXIT) "Stack is empty"
(elixir) lib/gen_server.ex:834: GenServer.call/3
iex(16)>
19:08:46.322 [error] GenServer Spvsr.More.Stack terminating
** (stop) "Stack is empty"
Last message (from #PID<0.115.0>): :pop
State: []
Client #PID<0.115.0> is alive
(stdlib) gen.erl:169: :gen.do_call/4
(elixir) lib/gen_server.ex:831: GenServer.call/3
(stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
(elixir) src/elixir.erl:233: :elixir.eval_forms/4
(iex) lib/iex/evaluator.ex:250: IEx.Evaluator.handle_eval/5
(iex) lib/iex/evaluator.ex:230: IEx.Evaluator.do_eval/3
(iex) lib/iex/evaluator.ex:208: IEx.Evaluator.eval/3
(iex) lib/iex/evaluator.ex:94: IEx.Evaluator.loop/1
> Spvsr.More.Stash.get_value []
> Spvsr.More.Stack.get # [1, 2, 3, 4]
Now, when the stack process restarts, it should take the value from the stash process, which is []
. Instead, it contains the value [1, 2 ,3, 4]
. Can someone please help me understand what am I doing wrong?