Current section
Files
Jump to
Current section
Files
lib/agentex.ex
defmodule Agentex do
@moduledoc ~S"""
Distributed `Agent` implementation, providing multi-node agents on top of Mnesia.
More on starting distributed apps:
http://engineering.pivotal.io/post/how-to-set-up-an-elixir-cluster-on-amazon-ec2/
More on `mnesia`:
http://learnyousomeerlang.com/mnesia
"""
use Application
require Logger
@sleep_time 1_000
@wait_for_nodes 3_000
@default_database Agentex.Simple
@default_bag Kv
@default_table Module.concat(@default_database, @default_bag)
defp nodes do
self = node()
nodes = :agentex
|> Application.get_env(:nodes, [self | Node.list])
|> Enum.filter(fn
^self -> false
_ -> true
end)
Logger.debug(fn -> "☆#{inspect node()}☆ ⇒ nodes are: #{inspect nodes}" end)
wait_for_nodes = Application.get_env(:agentex, :wait_for_nodes, @wait_for_nodes)
attempts = Integer.floor_div(wait_for_nodes, @sleep_time) + 1
Enum.any?(1..attempts, fn i ->
Process.sleep(@sleep_time)
Enum.each(nodes, &Node.connect/1)
Logger.debug(fn -> "Attempt ##{i}. Nodes: #{inspect Node.list}" end)
Enum.count(Node.list) >= Enum.count(nodes)
end)
nodes = [self | Node.list]
Logger.info(fn -> "★★★ Nodes connected: #{inspect nodes}" end)
i_am_chuck_norris = case Enum.sort(nodes) do
[^self | _] -> true
_ -> false
end
{i_am_chuck_norris, nodes}
end
def database!(Agentex.Simple), do: use(Agentex.DB)
def database!(_), do: :ok
defp initialize(_preparation) do
database = Application.get_env(:agentex, :database, Agentex.Simple)
# Amnesia.start
{i_am_chuck_norris, nodes} = nodes()
Agentex.database!(database)
if i_am_chuck_norris do
apply(database, :destroy, [])
apply(database, :create!, [[disk: nodes]]) # [[memory: nodes]])
end
Logger.info(fn -> "★★★ Tables used: #{inspect apply(database, :tables, [])}" end)
database
end
def start(type, args) do
Logger.warn fn -> "#{__MODULE__}.start(#{inspect type}, #{inspect args}) @ #{Node.self}" end
import Supervisor.Spec, warn: false
database = initialize(args)
Logger.warn fn -> "★★★ Starting: #{inspect apply(database, :tables, [])}" end
Supervisor.start_link(
# Enum.map(apply(database, :tables, []),
Enum.map([@default_table],
&worker(Agentex.Bond, [{database, &1}], id: Module.concat([Agentex, Bond, database, &1]))),
[strategy: :one_for_one, name: Agentex.Supervisor])
end
##############################################################################
@doc ~S"""
Retrieves the value for the given key and optional type.
## Examples
iex> Agentex.put(:pi, 3.14)
iex> Agentex.get!(:pi)
3.14
iex> Agentex.put(:rgb, %{r: 255, g: 0, b: 128})
iex> Agentex.get(:rgb)
%Agentex.Simple.Kv{key: :rgb, value: %{b: 128, g: 0, r: 255}}
iex> Agentex.put(:rgb, %{r: 255, g: 0, b: 128})
iex> Agentex.get(:rgb)
%Agentex.Simple.Kv{key: :rgb, value: %{r: 255, g: 0, b: 128}}
iex> Agentex.put(:rgb, %{r: 255, g: 0, b: 128})
iex> Agentex.get!(:rgb)
%{r: 255, g: 0, b: 128}
iex> Agentex.put(:rrr, [r: 255, r: 0, r: 128])
iex> Agentex.get!(:rrr)
[r: 255, r: 0, r: 128]
iex> Agentex.put(:rrr, [{:r, 255}, {:r, 0}, {:r, 128}])
iex> Agentex.get!(:rrr)
[r: 255, r: 0, r: 128]
"""
def get(name \\ Agentex.Namer.table(@default_table), key)
def get([name], key), do: get(name, key)
def get(name, key), do: Agentex.Bond.get(name, key)
def get!(name \\ Agentex.Namer.table(@default_table), key)
def get!([name], key), do: get!(name, key)
def get!(name, key), do: Agentex.Bond.get!(name, key)
def put(name \\ Agentex.Namer.table(@default_table), key, value)
def put([name], key, value), do: put(name, key, value)
def put(name, key, value), do: Agentex.Bond.put(name, key, value)
##############################################################################
end