Skip to content

Commit

Permalink
remove filters and maps when selecting syn group names by using diffe…
Browse files Browse the repository at this point in the history
…rent scopes

this redoes the previous commits by avoiding requiring a new syn api to
match only selected group names and instead use three different scopes
for child specs, nodes, and started children

having different scopes avoid the filters and remappings which were
previously done when retrieving group names
  • Loading branch information
flaviogrossi committed Apr 16, 2024
1 parent 2a2e759 commit 664b2d6
Showing 1 changed file with 61 additions and 82 deletions.
143 changes: 61 additions & 82 deletions lib/syn_supervisor/distribution.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,21 @@ defmodule SynSupervisor.Distribution do

@type child_mapper_t :: (Child.t() -> any())

@spec start(scope_t()) :: :ok
def start(scope) do
:syn.add_node_to_scopes([scope])
end

@spec start_and_join(scope_t()) :: :ok
def start_and_join(scope) do
start(scope)
:syn.join(scope, member_group(Node.self()), self())
# define and start 3 scopes:
# - one to store the nodes/supervisors which have joined
# - one to store the child specifications
# - one to store the childrend that have been started
# three different scopes are used to have faster lookups (to avoid calling
# :syn.group_names(scope) |> Enum.filter(filter_fun) to only get for
# example the child specs)
node_scope = node_scope(scope)
spec_scope = spec_scope(scope)
child_scope = child_scope(scope)
start([node_scope, spec_scope, child_scope])

:syn.join(node_scope, Node.self(), self())
end

@spec child_join(scope_t(), Child.id_t(), Node.t(), pid(), pid(), Child.spec_t()) ::
Expand All @@ -33,7 +39,7 @@ defmodule SynSupervisor.Distribution do
supervisor_pid: supervisor
}

case :syn.join(scope, child_group(child), child_pid) do
case :syn.join(child_scope(scope), child, child_pid) do
:ok ->
track_spec(scope, child_spec)
:ok
Expand Down Expand Up @@ -72,10 +78,7 @@ defmodule SynSupervisor.Distribution do

@spec list_children(scope_t()) :: list(Child.t())
def list_children(scope) do
child_groups(scope)
|> Enum.map(fn {:child, %Child{} = c} ->
c
end)
get_children(scope)
end

@spec find_spec(scope_t(), Child.id_t()) :: {:ok, Child.spec_t()} | {:error, :not_found}
Expand Down Expand Up @@ -131,7 +134,7 @@ defmodule SynSupervisor.Distribution do

@spec member_for_node(scope_t(), Node.t()) :: nil | pid()
def member_for_node(scope, node) do
case :syn.members(scope, member_group(node)) do
case :syn.members(node_scope(scope), node) do
[{member, _meta} | _] -> member
_ -> nil
end
Expand All @@ -143,12 +146,26 @@ defmodule SynSupervisor.Distribution do
{node, member_for_node(scope, node)}
end

@spec track_spec(scope_t(), Child.spec_t()) :: list(:ok | {:error, term()})
defp track_spec(scope, child_spec) do
scope
|> supervisors()
|> then(&multi_join(spec_scope(scope), child_spec, &1))
end

@spec untrack_spec(scope_t(), Child.spec_t()) :: list(:ok | {:error, term()})
def untrack_spec(scope, child_spec) do
scope
|> :syn.members(spec_group(child_spec))
|> Enum.map(fn {pid, _meta} -> pid end)
|> then(&multi_leave(scope, spec_group(child_spec), &1))
|> supervisors()
|> then(&multi_leave(spec_scope(scope), child_spec, &1))
end

defp multi_join(scope, group, pids) do
Enum.map(pids, &:syn.join(scope, group, &1))
end

defp multi_leave(scope, group, pids) do
Enum.map(pids, &:syn.leave(scope, group, &1))
end

@spec check_members(scope_t()) :: :ok
Expand Down Expand Up @@ -187,14 +204,14 @@ defmodule SynSupervisor.Distribution do
end
end

@spec find_child_with_fn(scope_t(), ({:child, Child.t()} -> boolean())) ::
@spec find_child_with_fn(scope_t(), (Child.t() -> boolean())) ::
{:ok, Child.t()} | {:error, :not_found}
defp find_child_with_fn(scope, fun) do
scope
|> child_groups()
|> get_children()
|> Enum.find_value(
{:error, :not_found},
fn {:child, c} ->
fn c ->
if fun.(c) do
{:ok, c}
else
Expand All @@ -204,85 +221,47 @@ defmodule SynSupervisor.Distribution do
)
end

@spec track_spec(scope_t(), Child.spec_t()) :: list(:ok | {:error, term()})
defp track_spec(scope, child_spec) do
scope
|> supervisors()
|> then(&multi_join(scope, spec_group(child_spec), &1))
end

defp multi_join(scope, group, pids) do
Enum.map(pids, &:syn.join(scope, group, &1))
end

defp multi_leave(scope, group, pids) do
Enum.map(pids, &:syn.leave(scope, group, &1))
end

@spec supervisors(scope_t()) :: list(pid())
defp supervisors(scope) do
scope
|> :syn.group_names()
|> Enum.filter(fn
{:member, _} -> true
_ -> false
end)
|> Enum.flat_map(&:syn.members(scope, &1))
|> get_nodes()
|> Enum.flat_map(&:syn.members(node_scope(scope), &1))
|> Enum.map(fn {pid, _meta} -> pid end)
|> Enum.uniq()
end

defp child_groups(scope) do
scope
|> :syn.group_names()
|> Enum.filter(fn
{:child, _child} -> true
_ -> false
end)
end

defp spec_group(child_spec) do
{:spec, child_spec}
end
@spec node_scope(scope_t()) :: scope_t()
defp node_scope(scope), do: :"#{scope}-node"

defp member_group(node) do
{:member, node}
end
@spec spec_scope(scope_t()) :: scope_t()
defp spec_scope(scope), do: :"#{scope}-spec"

defp child_group(%Child{} = c) do
{:child, c}
end
@spec child_scope(scope_t()) :: scope_t()
defp child_scope(scope), do: :"#{scope}-child"

defp get_nodes(scope) do
group_names_match(scope, :member)
@spec start(list(scope_t())) :: :ok
defp start(scopes) do
:syn.add_node_to_scopes(scopes)
end

@spec get_children(scope_t()) :: list(Child.t())
defp get_children(scope) do
group_names_match(scope, :child)
scope
|> child_scope()
|> :syn.group_names()
end

defp get_specs(scope) do
group_names_match(scope, :spec)
@spec get_nodes(scope_t()) :: list(atom())
defp get_nodes(scope) do
scope
|> node_scope()
|> :syn.group_names()
end

defp group_names_match(scope, match) do
node_param = :_

case :syn_backbone.get_table_name(:syn_pg_by_name, scope) do
:undefined ->
raise "cannot get table name"

table_by_name ->
duplicated_groups =
:ets.select(table_by_name, [
{
{{{:"$1", :"$2"}, :_}, :_, :_, :_, node_param},
[{:==, :"$1", match}],
[:"$2"]
}
])

duplicated_groups |> :ordsets.from_list() |> :ordsets.to_list()
end
@spec get_specs(scope_t()) :: list(Child.spec_t())
defp get_specs(scope) do
scope
|> spec_scope()
|> :syn.group_names()
end
end

0 comments on commit 664b2d6

Please sign in to comment.