defmodule CPSolver.Distributed do
require Logger
def call(node, solver, mod, function, args) do
:erpc.call(node, mod, function, [solver | args])
end
def worker_nodes(node_list \\ [Node.self() | Node.list()]) do
node_list
end
def choose_worker_node(nodes \\ worker_nodes())
def choose_worker_node(true) do
worker_nodes()
|> choose_worker_node()
end
def choose_worker_node(distributed?) when not distributed? do
Node.self()
end
def choose_worker_node(node_list) when is_list(node_list) do
Enum.random(node_list)
end
def test() do
alias CPSolver.Examples.{Sudoku, Queens}
Logger.configure(level: :info)
leader_node = :leader
if Node.self() |> to_string() |> String.starts_with?(to_string(leader_node)) do
## cluster is up, skip initialization
:ok
else
Node.start(leader_node, :shortnames)
Node.set_cookie(:solver)
# Run the solver with the model that takes noticeable time to complete.
_worker_nodes = Enum.map(["node1", "node2", "node3"], fn node ->
{:ok, _pid, node_name} = :peer.start(%{name: node, longnames: false, args: [~c"-setcookie", ~c"solver"]})
:erpc.call(node_name, :code, :add_paths, [:code.get_path()])
node_name
end)
end
# Run the solver with the model that takes noticeable time to complete.
my_pid = self()
## Distributed Sudoku
Logger.notice("Sudoku test: starting")
five_solutions_sudoku = Sudoku.puzzles().s9x9_5
{:ok, _res} = CPSolver.solve(Sudoku.model(five_solutions_sudoku),
solution_handler: fn solution ->
send(my_pid, {Node.self(), solution})
end,
distributed: Enum.shuffle(Node.list())
)
verify_solutions(5)
Logger.notice("Sudoku test: ok")
## Distributed Queens
Logger.notice("Queens test: starting")
{:ok, _res} = CPSolver.solve(Queens.model(8),
solution_handler: fn solution ->
send(my_pid, {Node.self(), solution})
end,
distributed: Enum.shuffle(Node.list())
)
verify_solutions(92)
Logger.notice("Queens test: ok")
true
end
def verify_solutions(expected_count) do
solutions = CPSolver.Utils.flush()
^expected_count = length(solutions)
true =
solutions
|> MapSet.new(fn {node, _solution} -> node end)
|> tap(fn nodes -> Logger.info("Nodes with solutions: #{Enum.join(nodes, "")}") end)
|> MapSet.subset?(MapSet.new(Node.list()))
end
end