Last active
August 7, 2023 21:37
-
-
Save andrewhao/c90f3d12cc92c0356f7d2d7173289071 to your computer and use it in GitHub Desktop.
Dynamic Supervisors in Elixir
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Game do | |
use GenServer | |
def init(game_id) do | |
{:ok, %{game_id: game_id}} | |
end | |
def start_link(game_id) do | |
GenServer.start_link(__MODULE__, game_id, name: {:global, "game:#{game_id}"}) | |
end | |
def add_player(pid, player_name) do | |
GenServer.call(pid, {:add_player, player_name}) | |
end | |
def handle_call({:add_player, player_name}, _from, %{game_id: game_id} = state) do | |
# Uh oh, we started this process but it's not under supervision! | |
start_status = Player.start_link({player_name, game_id}) | |
{:reply, start_status, state} | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule SupervisedGame do | |
# ... | |
def handle_call({:add_player, player_name}, _from, %{game_id: game_id} = state) do | |
# Now we replace this with supervised management | |
start_status = PlayerSupervisor.add_player(player_name, game_id) | |
{:reply, start_status, state} | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Player do | |
use GenServer | |
def init({player_name, game_id}) do | |
{:ok, %{name: player_name, game_id: game_id}} | |
end | |
def start_link({player_name, game_id}) do | |
GenServer.start_link( | |
__MODULE__, | |
{player_name, game_id}, | |
name: {:global, "player:#{player_name}"} | |
) | |
end | |
def get(pid) do | |
GenServer.call(pid, :get) | |
end | |
def handle_call(:get, _from, state) do | |
{:reply, {:ok, state}, state} | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule PlayerDynamicSupervisor do | |
use DynamicSupervisor | |
def start_link(_arg) do | |
DynamicSupervisor.start_link(__MODULE__, :ok, name: __MODULE__) | |
end | |
def init(:ok) do | |
DynamicSupervisor.init(strategy: :one_for_one) | |
end | |
# Start a Player process and add it to supervision | |
def add_player(player_name, game_id) do | |
# Note that start_child now directly takes in a child_spec. | |
child_spec = {Player, {player_name, game_id}} | |
# Equivalent to: | |
# child_spec = Player.child_spec({player_name, game_id}) | |
DynamicSupervisor.start_child(__MODULE__, child_spec) | |
end | |
# Terminate a Player process and remove it from supervision | |
def remove_player(player_pid) do | |
DynamicSupervisor.terminate_child(__MODULE__, player_pid) | |
end | |
# Nice utility method to check which processes are under supervision | |
def children do | |
DynamicSupervisor.which_children(__MODULE__) | |
end | |
# Nice utility method to check which processes are under supervision | |
def count_children do | |
DynamicSupervisor.count_children(__MODULE__) | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Player do | |
# ... | |
# Insert this start_link/2 method, which intercepts the extra `[]` | |
# argument from the Supervisor and molds it back to correct form. | |
def start_link([], {player_name, game_id}) do | |
start_link({player_name, game_id}) | |
end | |
def start_link({player_name, game_id}) do | |
# Original implementation... | |
end | |
# ... | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule PlayerSupervisor do | |
use Supervisor | |
def start_link([]) do | |
Supervisor.start_link(__MODULE__, :ok, name: __MODULE__) | |
end | |
def init(:ok) do | |
Supervisor.init([Player], strategy: :simple_one_for_one) | |
end | |
# Start a Player process and add it to supervision | |
def add_player(player_name, game_id) do | |
# Note that the second arg to start_child/2 must be an Enumerable | |
Supervisor.start_child(__MODULE__, [{player_name, game_id}]) | |
end | |
# Terminate a Player process and remove it from supervision | |
def remove_player(player_pid) do | |
Supervisor.terminate_child(__MODULE__, player_pid) | |
end | |
# Nice utility method to check which processes are under supervision | |
def children do | |
Supervisor.which_children(__MODULE__) | |
end | |
# Nice utility method to check which processes are under supervision | |
def count_children do | |
Supervisor.count_children(__MODULE__) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I am not sure, but probably the correct
start_link
signature inPlayer
module should look like thisdef start_link([player_name, game_id])
. Anyway that was the issue in my case, may be will help somebody.