Skip to content

Instantly share code, notes, and snippets.

@DarinM223
Last active June 5, 2017 05:14
Show Gist options
  • Save DarinM223/f61b4c711c87a77db75ed4a8c899da17 to your computer and use it in GitHub Desktop.
Save DarinM223/f61b4c711c87a77db75ed4a8c899da17 to your computer and use it in GitHub Desktop.
OTP stash worker state management example
defmodule Sequence.Server do
use GenServer
# Public API
def start_link(agent_id) do
GenServer.start_link(__MODULE__, agent_id, name: __MODULE__)
end
def next_number do
GenServer.call(Sequence.Server, :next_number)
end
def increment_number(delta) do
GenServer.cast(Sequence.Server, {:increment_number, delta})
end
# GenServer API
def init(agent_id) do
# Load saved value from agent.
current_number = Agent.get(agent_id, &(&1))
{:ok, {current_number, agent_id}}
end
def handle_call(:next_number, _from, {current_number, agent_id}) do
{:reply, current_number, {current_number + 1, agent_id}}
end
def handle_cast({:increment_number, delta}, {current_number, agent_id}) do
{:noreply, {current_number + delta, agent_id}}
end
def terminate(_reason, {current_number, agent_id}) do
# Save value to agent when process terminates (like when an exception is thrown).
Agent.update(agent_id, fn _ -> current_number end)
end
end
defmodule Sequence.Subsupervisor do
use Supervisor
def start_link(agent_id) do
children = [worker(Sequence.Server, [agent_id])]
Supervisor.start_link(children, strategy: :one_for_one)
end
end
defmodule Sequence.Supervisor do
use Supervisor
def start_link(init_number) do
result = {:ok, pid} = Supervisor.start_link(__MODULE__, [])
start_workers(init_number, pid)
result
end
def start_workers(init_number, pid) do
{:ok, agent_id} = Agent.start(fn -> init_number end)
Supervisor.start_child(pid, supervisor(Sequence.Subsupervisor, [agent_id]))
end
def init(_) do
supervise([], strategy: :one_for_one)
end
end
iex(1)> Sequence.Supervisor.start_link(123)
{:ok, #PID<0.108.0>}
iex(2)> Sequence.Server.next_number
123
iex(3)> Sequence.Server.next_number
124
iex(4)> Sequence.Server.increment_number(5)
:ok
iex(5)> Sequence.Server.next_number
130
iex(6)> Sequence.Server.increment_number("hello") # Cause an exception in Server
:ok
iex(7)>
05:52:54.592 [error] GenServer Sequence.Server terminating
** (ArithmeticError) bad argument in arithmetic expression
(sequence) lib/sequence/server.ex:18: Sequence.Server.handle_cast/2
(stdlib) gen_server.erl:601: :gen_server.try_dispatch/4
(stdlib) gen_server.erl:667: :gen_server.handle_msg/5
(stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Last message: {:"$gen_cast", {:increment_number, "hello"}}
State: {131, #PID<0.109.0>}
nil
iex(8)> Sequence.Server.next_number # Server recovered state after restarting
131
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment