View Source Setting up a Cluster
Horde doesn't provide functionality to set up your cluster, we recommend you use libcluster for this purpose.
There are three strategies you can use to integrate libcluster with Horde:
Automatic Cluster Membership
When starting a Horde.Registry or Horde.DynamicSupervisor, setting the members
option to have a value of :auto will automate membership management. In this
mode, all visible nodes will be initially added to the cluster. In addition,
any new nodes that become visible will be automatically added and any
nodes that shut down will be automatically removed.
Static Cluster Membership
If you will not be adding or removing members from the cluster dynamically, then you can set up libcluster and tell Horde about the members of your cluster. For example, if you run your cluster on bare metal hardware and have a fixed number of servers.
supervisor_members = [
{MyHordeSupervisor, :node1},
{MyHordeSupervisor, :node2},
{MyHordeSupervisor, :node3},
{MyHordeSupervisor, :node4}
]
registry_members = [
{MyHordeRegistry, :node1},
{MyHordeRegistry, :node2},
{MyHordeRegistry, :node3},
{MyHordeRegistry, :node4}
]
children = [
{Horde.Registry, name: MyHordeRegistry, keys: :unique, members: registry_members},
{Horde.DynamicSupervisor, name: MyHordeSupervisor, strategy: :one_for_one, members: supervisor_members},
...
]This is the simplest approach. You tell Horde which members are supposed to be in the cluster, and if they are available, Horde will include them in the cluster.
Dynamic Cluster Membership
If you will be adding and removing nodes from your cluster constantly, and don't want to repackage your application every time you do this, then you will need to perform a couple of extra steps (assuming your needs cannot be met by the :auto setting).
In this scenario, you will need to implement a module-based Supervisor
defmodule MyHordeSupervisor do
use Horde.DynamicSupervisor
def start_link(_) do
Horde.DynamicSupervisor.start_link(
__MODULE__,
[strategy: :one_for_one],
name: __MODULE__
)
end
def init(init_arg) do
[members: members()]
|> Keyword.merge(init_arg)
|> Horde.DynamicSupervisor.init()
end
defp members() do
[Node.self() | Node.list()]
|> Enum.map(fn node -> {__MODULE__, node} end)
end
endNow every time MyHordeSupervisor gets started or restarted, it will compute the members based on the currently connected members.
In this scenario, you may also want to implement a module-based Registry
defmodule MyHordeRegistry do
use Horde.Registry
def start_link(_) do
Horde.Registry.start_link(__MODULE__, [keys: :unique], name: __MODULE__)
end
def init(init_arg) do
[members: members()]
|> Keyword.merge(init_arg)
|> Horde.Registry.init()
end
defp members() do
[Node.self() | Node.list()]
|> Enum.map(fn node -> {__MODULE__, node} end)
end
endNow every time MyHordeRegistry gets started or restarted, it will compute the members based on the currently connected members.
We also need a separate process that will listen for {:nodeup, node} and {:nodedown, node} events and adjust the members of the Horde cluster accordingly. Put this in your supervision tree underneath MyHordeSupervisor.
defmodule NodeListener do
use GenServer
def start_link(_), do: GenServer.start_link(__MODULE__, [])
def init(_) do
:net_kernel.monitor_nodes(true, node_type: :visible)
{:ok, nil}
end
def handle_info({:nodeup, _node, _node_type}, state) do
set_members(MyHordeRegistry)
set_members(MyHordeSupervisor)
{:noreply, state}
end
def handle_info({:nodedown, _node, _node_type}, state) do
set_members(MyHordeRegistry)
set_members(MyHordeSupervisor)
{:noreply, state}
end
defp set_members(name) do
members =
[Node.self() | Node.list()]
|> Enum.map(fn node -> {name, node} end)
:ok = Horde.Cluster.set_members(name, members)
end
endNote that the funcionality provided in this example is essentially the same as
the members: :auto setting, however setting
it up yourself allows greater flexability to modify it if :auto mode doesn't
meet your requirements.