Current section
Files
Jump to
Current section
Files
lib/strategy/strategy.ex
defmodule Cluster.Strategy do
@moduledoc """
This module defines the behaviour for implementing clustering strategies.
"""
defmacro __using__(_) do
quote do
@behaviour Cluster.Strategy
end
end
@type topology :: atom()
@type bad_nodes :: [{node(), reason :: term()}]
@type mfa_tuple :: {module(), atom(), [term()]}
@type strategy_opts :: [topology: atom(),
connect: mfa_tuple,
disconnect: mfa_tuple]
# Starts the strategy
@callback start_link(strategy_opts) :: {:ok, pid} | :ignore | {:error, reason :: term}
@doc """
Given a list of node names, attempts to connect to all of them.
Returns `:ok` if all nodes connected, or `{:error, [{node, reason}, ..]}`
if we failed to connect to some nodes.
All failures are logged.
"""
@spec connect_nodes(topology, mfa_tuple, [atom()]) :: :ok | {:error, bad_nodes}
def connect_nodes(topology, {mod, fun, args}, nodes) when is_list(nodes) do
bad_nodes = Enum.reduce(nodes, [], fn n, acc ->
fargs = args++[n]
unless :erlang.function_exported(mod, fun, length(fargs)) do
fstr = "#{mod}.#{fun}/#{length(fargs)}"
raise "#{fstr} is undefined!"
end
case apply(mod, fun, fargs) do
true ->
Cluster.Logger.info topology, "connected to #{inspect n}"
acc
false ->
Cluster.Logger.warn topology, "unable to connect to #{inspect n}"
[{n, false}|acc]
:ignored ->
Cluster.Logger.warn topology, "unable to connect to #{inspect n}: not part of network"
[{n, :ignored}|acc]
end
end)
case bad_nodes do
[] -> :ok
_ -> {:error, bad_nodes}
end
end
@doc """
Given a list of node names, attempts to disconnect from all of them.
Returns `:ok` if all nodes disconnected, or `{:error, [{node, reason}, ..]}`
if we failed to disconnect from some nodes.
All failures are logged.
"""
@spec disconnect_nodes(topology, mfa_tuple, [atom()]) :: :ok | {:error, bad_nodes}
def disconnect_nodes(topology, {mod, fun, args}, nodes) when is_list(nodes) do
bad_nodes = Enum.reduce(nodes, [], fn n, acc ->
fargs = args++[n]
unless :erlang.function_exported(mod, fun, length(fargs)) do
fstr = "#{mod}.#{fun}/#{length(fargs)}"
raise "#{fstr} is undefined!"
end
case apply(mod, fun, fargs) do
true ->
Cluster.Logger.info topology, "disconnected from #{inspect n}"
acc
false ->
Cluster.Logger.warn topology, "disconnect from #{inspect n} failed because we're already disconnected"
acc
:ignored ->
Cluster.Logger.warn topology, "disconnect from #{inspect n} failed because it is not part of the network"
acc
end
end)
case bad_nodes do
[] -> :ok
_ -> {:error, bad_nodes}
end
end
end