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 utils.ex
Raw

lib/beethoven/utils.ex

defmodule Beethoven.Utils do
@moduledoc """
Module for generic utilities.
"""
require Logger
#
#
#
@doc """
Fn to simplify calling named Processes on other nodes... and getting a call back.
"""
@spec remote_call((-> any()), node(), integer()) :: any() | {:error, :timeout}
def remote_call(fun, host, timeout \\ 1_000) do
# generate seed for tracking (100_000-999_9999)
seed = :rand.uniform(900_000) + 99_999
# ownPID
ownPid = self()
#
# Fun that will be ran in the remote host
remote_fun = fn ->
# run input fn
result = fun.()
# Send back to original caller
_ = send(ownPid, {:remote_call, seed, result})
end
# Spawn PID on remote node
_ = Node.spawn(host, remote_fun)
# Await response
receive do
{:remote_call, ^seed, response} ->
response
after
# Timeout awaiting response
timeout ->
{:error, :timeout}
end
end
#
#
#
@doc """
Gets an environmental variable from beethoven's application config
"""
def get_app_env(envVar, default \\ nil) do
Application.fetch_env(:beethoven, envVar)
|> case do
# Got roles from application env
{:ok, value} ->
value
# No roles found, return empty list.
:error ->
Logger.warning("{:#{envVar}} is not set in config/*.exs. Using:",
default_value: default
)
default
end
end
#
#
#
@doc """
The same as `backoff/3` but shows the calling service's name in the verbose output.
Performs a backoff wait to void race conditions in a distributed environment.
(:rand.uniform(1..max) + delta) * multiplier
"""
@spec backoff_n(atom(), integer(), integer(), integer()) :: :ok
def backoff_n(name, max \\ 20, delta \\ 0, multiplier \\ 1) when is_atom(name) do
# backoff in milliseconds
backoff = (:rand.uniform(max) + delta) * multiplier
Logger.info("[#{name}] Backoff for (#{backoff |> Integer.to_string()}) milliseconds started.")
Process.sleep(backoff)
end
#
#
#
@doc """
Performs a backoff wait to void race conditions in a distributed environment.
(:rand.uniform(1..max) + delta) * multiplier
"""
@spec backoff(integer(), integer(), integer()) :: :ok
def backoff(max \\ 20, delta \\ 0, multiplier \\ 1) do
# backoff in milliseconds
backoff = (:rand.uniform(max) + delta) * multiplier
Logger.info("Backoff for (#{backoff |> Integer.to_string()}) milliseconds started.")
Process.sleep(backoff)
end
#
#
#
@doc """
Toggles the monitoring status of another node in the cluster.
"""
@spec monitor_node(node(), boolean()) :: :ok
def monitor_node(nodeName, mode) do
Logger.info("Monitoring on node (#{nodeName}) has been set to (#{mode}).")
true = Node.monitor(nodeName, mode)
:ok
end
#
#
#
@doc """
Toggles the monitoring status of **all** nodes in the cluster.
"""
@spec monitor_all_nodes(boolean()) :: :ok
def monitor_all_nodes(mode) do
list = Node.list()
Logger.info("Monitoring all (#{length(list) |> to_string}) node(s) in the cluster.")
Node.list()
|> Enum.each(fn node ->
true = Node.monitor(node, mode)
end)
end
#
#
end