Sovite.Cluster (sovite v0.6.0)

Copy Markdown View Source

A cluster of BEAM nodes over Partisan, without Distributed Erlang.

:ok =
  Sovite.Cluster.start(
    name: :"mx1@10.0.0.1",
    ip: {10, 0, 0, 1},
    port: 4370,
    tls: [cert_file: "node.pem", key_file: "node.key", ca_file: "cluster-ca.pem"]
  )

{:ok, peer} = Sovite.Cluster.Peer.parse("mx2@10.0.0.2:4370")
:ok = Sovite.Cluster.join(peer)

Sovite.Cluster.send(:"mx2@10.0.0.2", MyApp.Server, {:hello, Sovite.Cluster.node()})

Every node is connected to every other one (Partisan's full mesh manager), over TLS 1.3 with mutual authentication: a node accepts only peers whose certificate the cluster CA (:ca_file) signed, and presents its own. There is no plaintext mode. Peers can send any message to any registered process, so the cluster CA must sign the certificates of cluster nodes and nothing else, and the peer port should still only be reachable from the other nodes.

Partisan is one per VM: start/1 configures and starts the :partisan application, and only one cluster runs at a time.

Messages go to processes registered by name on the other node (see send/4); they are delivered as sent, and like Erlang messages, they can be lost when a connection drops. call/4 waits for a reply that the receiving process sends with reply/2. monitor_nodes/1 subscribes to {:nodeup, node} and {:nodedown, node} messages. Sovite.Cluster.Joiner keeps joining a list of known peers.

Messages are on two channels: :control, for small messages, and :bulk (send/4 with channel: :bulk), for large ones such as message files, so they do not hold up the others.

Summary

Types

A request's sender, for reply/2.

Functions

Sends request to the process registered as name on node, and waits for its reply. The process gets {:"$cluster_call", from, request}, and answers with reply/2.

The other members this node is connected to.

Whether this node is connected to node.

Joins the cluster peer is in.

Removes node from the cluster, or this node when node is this node: its peers forget it, and it forgets them.

The members of the cluster, this node included, whether connected or not.

Subscribes the calling process to {:nodeup, node} and {:nodedown, node} messages, or unsubscribes it.

This node's name.

Sends message to the process registered as name on node. Returns {:error, reason} when it could not be handed to a connection, mostly because node is not connected.

Configures and starts Partisan.

Whether Partisan runs.

Stops Partisan. Not from a process that an application stop is waiting for.

Types

from()

@opaque from()

A request's sender, for reply/2.

Functions

call(node, name, request, timeout \\ 5000, opts \\ [])

@spec call(node(), atom(), term(), timeout(), keyword()) ::
  {:ok, term()} | {:error, term()}

Sends request to the process registered as name on node, and waits for its reply. The process gets {:"$cluster_call", from, request}, and answers with reply/2.

Returns {:ok, reply}, or {:error, :timeout}, or {:error, reason} when the request could not be sent. A reply that comes after the timeout stays in the caller's mailbox as {:"$cluster_reply", tag, reply}. Takes the options of send/4.

connected()

@spec connected() :: [node()]

The other members this node is connected to.

connected?(node)

@spec connected?(node()) :: boolean()

Whether this node is connected to node.

join(peer)

@spec join(Sovite.Cluster.Peer.t()) :: :ok | {:error, term()}

Joins the cluster peer is in.

leave(node)

@spec leave(node()) :: :ok | {:error, term()}

Removes node from the cluster, or this node when node is this node: its peers forget it, and it forgets them.

members()

@spec members() :: [node()]

The members of the cluster, this node included, whether connected or not.

monitor_nodes(flag)

@spec monitor_nodes(boolean()) :: :ok | {:error, term()}

Subscribes the calling process to {:nodeup, node} and {:nodedown, node} messages, or unsubscribes it.

node()

@spec node() :: node()

This node's name.

reply(arg, reply)

@spec reply(from(), term()) :: :ok | {:error, term()}

Answers a call/4.

send(node, name, message, opts \\ [])

@spec send(node(), atom(), term(), keyword()) :: :ok | {:error, term()}

Sends message to the process registered as name on node. Returns {:error, reason} when it could not be handed to a connection, mostly because node is not connected.

Options

  • :channel - :control (the default) or :bulk, for large messages.

start(opts)

@spec start(keyword()) :: :ok | {:error, term()}

Configures and starts Partisan.

Options

  • :name - the node's name, an atom such as :"mx1@10.0.0.1". Required.
  • :ip - the address to accept peers on. Required.
  • :port - the port to accept peers on. Required.
  • :tls - :cert_file and :key_file, the node's certificate and key, and :ca_file, the cluster CA. Required.
  • :max_message_size - the largest message, in bytes, a peer may send. Defaults to 64 MB.

Returns {:error, :already_started} if Partisan runs already.

started?()

@spec started?() :: boolean()

Whether Partisan runs.

stop()

@spec stop() :: :ok

Stops Partisan. Not from a process that an application stop is waiting for.