The reference is this lesson. We can start a GenServer "on-the-fly" when using an atom as identifier. For example:
Order.start_link(name: :a)
Every client function is then called using this given name: Order.deliver(:a)
defmodule Order do
use GenServer
def start_link(name: name) do
{:ok, _pid} = GenServer.start_link(__MODULE__, name, name: name)
end
def show(name), do: GenServer.call(name, :show)
def add(name, value), do: GenServer.call(name, {:add, value})
def deliver(name), do: GenServer.cast(name, :deliver)
def init(name = _state), do: {:ok, {name, :init, 0}}
def handle_call(:show, _from, {_n, step, value} = state) do
{:reply, {step, value}, state}
end
[... server callbacks...]
end
You need to use an atom to name it (or its PID or a global
name or use :via
). The docs on name registration explain this.
We want to supervise this process once the "name" is determined.
We have to use the Registry and a dynamic supervisor. This supervisor and this Registry are in the supervision tree, instantiated here in the Application module.
use Application
def start(_, _) do
[
{Registry, keys: :unique, name: MyRegistry},
{DynamicSupervisor, name: MyDynSup}
]
|> Supervisor.start_link(opts)
end
You use the :via
strategy and supervise the previous "Order" module. Each new process is started "manually" with start_child
.
Create a new module with the modified client functions where the reference to the argument "name" in the client functions is replaced by "via_tuple(name)" as below. The new module wraps the original "Order" GenServer: just reformulate the client functions as below. The server functions are those defined in the "Order" module.
defmodule DynOrder do
defp via_tuple(name) do
{:via, Registry, {MyRegistry, name}}
end
def new(name) do
DynamicSupervisor.start_child(
DynSup,
{Order, [name: via_tuple(name)]}
)
end
def show(name), do: via_tuple(name) |> Order.show()
def add(name, value), do: via_tuple(name) |> Order.add(value)
def deliver(name), do: via_tuple(name) |> Order.deliver()
end
We can now start the new dynamic supervised GenServer with any string, say: DynOrder.new("a")
or DynOrder.new("b")
.
Then we can do: DynOrder.deliver("a")
or DynOrder.show("b")
.
We can programmatically assign names and run many concurrent supervised processes:
Enum.each(1..100, fn i ->
DynOrder.new("a-#{i}");
DynOrder.deliver("a_#{i}")
end)
You can even go one step further with partition supervisor.
Top comments (0)