Packages

A Decentralized failover and peer-to-peer node finder for Elixir. Allows Elixir nodes to find each other automatically. Once connected, they can coordinate to delegate roles and tasks between the nodes in the cluster. Written using only the Elixir and Erlang standard library.

Current section

Files

Jump to
beethoven lib beethoven role_mgmt manager.ex
Raw

lib/beethoven/role_mgmt/manager.ex

defmodule Beethoven.RoleMgmt.Manager do
@moduledoc """
Dynamic supervisor that manages the specialized role PIDs.
# Public API
- `add_role/3` Adds a role to the Manager Dynamic Supervisor. Stores role name + PID in ETS.
- `remove_role/1` Removes a hosted role from the Dynamic Supervisor.
The use of ETS is just for tracking atomic role names to PIDs.
It has no awareness of the cluster level allocations for this role.
"""
require Logger
use DynamicSupervisor
alias Beethoven.Signals
alias __MODULE__.Table, as: ManagerTable
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# DynamicSupervisor functions
#
#
#
@doc """
Supervisor Entry point.
"""
@spec start_link(any()) :: GenServer.on_start()
def start_link(init_args \\ []) do
DynamicSupervisor.start_link(__MODULE__, init_args, name: __MODULE__)
end
#
#
@impl true
def init(_init_arg) do
Logger.info(status: :startup)
_ = :ets.new(ManagerTable, [:set, :public, :named_table])
DynamicSupervisor.init(strategy: :one_for_one)
end
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# Public API functions
#
#
#
@doc """
Adds a role to the `#{__MODULE__}` Dynamic Supervisor. Adds the pid + roleName to ETS for tracking.
If the role already exists, `:ok` will still be returned.
"""
@spec add_role(atom(), module(), any()) :: :ok
def add_role(roleName, roleModule, args) do
Logger.info(operation: :add_role, role: roleName, role_mod: roleModule)
# Check if the role is already running
:ets.lookup(ManagerTable, roleName)
|> case do
# not running
[] ->
{:ok, pid} = DynamicSupervisor.start_child(__MODULE__, {roleModule, args})
Signals.increment_beethoven_role_count()
true = :ets.insert(ManagerTable, {roleName, pid, DateTime.now!("Etc/UTC")})
#
:ok
# running on the node
_ ->
:ok
end
end
#
#
@doc """
Removes the role from the `#{__MODULE__}`Dynamic Supervisor.
"""
@spec remove_role(atom()) :: :ok | {:error, :not_found}
def remove_role(roleName) do
:ets.lookup(ManagerTable, roleName)
|> case do
[{^roleName, pid, _}] ->
result = DynamicSupervisor.terminate_child(__MODULE__, pid)
Signals.decrement_beethoven_role_count()
result
[] ->
{:error, :not_found}
end
end
#
#
end