Skip to content

Instantly share code, notes, and snippets.

@keathley
Last active May 9, 2019 03:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save keathley/36e536e7b55d7444752bd29fb8d25d4d to your computer and use it in GitHub Desktop.
Save keathley/36e536e7b55d7444752bd29fb8d25d4d to your computer and use it in GitHub Desktop.
stateful_server example
defmodule Demo do
def demo do
_once_per_second = Demo.start(:once, 200)
_ten_per_second = Demo.start(:ten, 220)
_hundret_per_second = Demo.start(:hundret, 250)
IO.puts("Launced processes")
Process.sleep(1_500)
[:once, :ten, :hundret] |> show() |> IO.inspect()
Process.sleep(1_500)
[:once, :ten, :hundret] |> show() |> IO.inspect()
IO.puts("Killing processes")
:once |> DeviceAuthenticator.kill_worker()
:ten |> DeviceAuthenticator.kill_worker()
:hundret |> DeviceAuthenticator.kill_worker()
Process.sleep(1_500)
:once |> DeviceAuthenticator.kill_state()
:ten |> DeviceAuthenticator.kill_state()
:hundret |> DeviceAuthenticator.kill_state()
Process.sleep(1_500)
:once |> DeviceAuthenticator.kill_worker()
:ten |> DeviceAuthenticator.kill_worker()
:hundret |> DeviceAuthenticator.kill_worker()
Process.sleep(1_500)
[:once, :ten, :hundret] |> show() |> IO.inspect()
[:once, :ten, :hundret]
end
def start(name, interval) when is_integer(interval) do
args = [
name: name,
interval: interval,
counter: 0,
]
with {:ok, pid} <- DeviceAuthenticator.start_link(args) do
pid
end
end
def show(supervisors) do
supervisors
|> Enum.map(fn sup ->
sup
|> DeviceAuthenticator.get()
|> Integer.to_string()
end)
end
end
defmodule DeviceAuthenticator do
use Supervisor
alias DeviceAuthenticator.{
Store,
Worker,
}
@interval 1_000
@counter 0
def start_link(args \\ []) do
name = Keyword.get(args, :name, __MODULE__)
Supervisor.start_link(__MODULE__, args, name: name)
end
def init(args) do
name = Keyword.get(args, :name, :device_authenticator)
interval = Keyword.get(args, :interval, @interval)
counter = Keyword.get(args, :counter, @counter)
children = [
{Store, %{counter: counter, name: store_name(name)}},
{Worker, %{interval: interval, name: worker_name(name), store: store_name(name)}}
]
Supervisor.init(children, strategy: :one_for_one)
end
def get(name) do
name
|> store_name
|> Agent.get(& &1)
end
def kill_state(name) do
name
|> store_name
|> Process.whereis()
|> Process.exit(:kill)
end
def kill_worker(name) do
name
|> worker_name
|> Process.whereis
|> Process.exit(:kill)
end
def worker_name(name), do: :"#{name}_worker"
def store_name(name), do: :"#{name}_store"
end
defmodule DeviceAuthenticator.Worker do
use GenServer, restart: :transient
alias StatefulServer.Store
def start_link(state) do
name = state[:name]
GenServer.start_link(__MODULE__, state, name: name)
end
def init(state) do
Process.send_after(self(), :tick, state.interval)
{:ok, state}
end
def handle_info(:tick, %{store: store, interval: interval}=state) do
:ok = Store.increment(store)
Process.send_after(self(), :tick, interval)
{:noreply, state}
end
end
defmodule DeviceAuthenticator.Store do
use Agent
def start_link(%{counter: counter, name: name}) do
Agent.start_link(fn -> counter end, name: name)
end
def increment(store) do
Agent.update(store, & &1 + 1)
end
def get(store) do
Agent.get(store, & &1)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment