What is an easy way to form a dynamic cluster?

Is there an easy way to form a BEAM cluster with dynamic membership? Specifically I want to form the cluster from multiple instances of the same escript/program, this means that the name of each node needs to be unique, but names cannot be pre-generated. I could try to connect with sequentially increasing names, but is there an easier/better way? It seems like I could use https://github.com/bitwalker/libcluster but I don’t see a Cluster.Strategy that seems appropriate

3 Likes

Okay, I’ve come up with this little snippet. Is there anything that could be improved with it?

  def start_node(number) when number > 20, do: raise "Too many instances found"
  def start_node(number \\ 0) do
    node_name = node_name(number)

    case Node.start(node_name, :shortnames) do
      {:error, _error} ->
        start_node(number + 1)

      {:ok, _pid} ->
        IO.puts("Started node with name #{inspect(node_name)}")
        Node.set_cookie(node_name, :cookie)

        for num <- Range.new(0, number) do
          Node.connect(node_name)
        end
    end
  end

  def node_name(number) do
    {:ok, hostname} = :inet.gethostname()

    "elixirls-#{number}@#{hostname}"
    |> String.to_atom()
  end

Edit: added a max number

2 Likes

From what I remember, PragDave suggests the usage of long names instead of short names for nodes. Unless this is strictly for a local machine, I would go the extra mile and use long names because this way your code will work across different machines.

Another thing I noticed is the usage of String.to_atom. Depending on your use case, you may want to consider the usage of String.to_existing_atom (https://til.hashrocket.com/posts/gkwwfy9xvw-converting-strings-to-atoms-safely)

Thus far, these are the only 2 things I can think about. YMMV but I hope these ideas are of some use to you.

This is only for a local machine, so I think short names should be fine, in fact if I could I would ignore the hostname completely in this code. Also Node.start/3 and Node.connect/1 both only accept atoms so the only way that I see to avoid String.to_atom/1 is to pregenerate a set number of node names. Which could be a good idea, but if I were to do that I’d prefer to instead just limit the number that start_node will recurse to, a max of 20 would probably be a good idea.

1 Like