Clustering

View Source

Mesh uses capabilities to route processes to appropriate nodes in a cluster.

Setup libcluster

For multi-node setups, configure libcluster in your application:

defmodule MyApp.Application do
  use Application

  def start(_type, _args) do
    topologies = [
      gossip: [
        strategy: Cluster.Strategy.Gossip,
        config: [
          port: 45892,
          if_addr: "0.0.0.0",
          multicast_addr: "230.1.1.251",
          multicast_ttl: 1
        ]
      ]
    ]

    children = [
      # Start libcluster for node discovery
      {Cluster.Supervisor, [topologies, [name: MyApp.ClusterSupervisor]]},
      # Start Mesh
      Mesh.Supervisor,
      # Your workers...
    ]

    Supervisor.start_link(children, strategy: :one_for_one)
  end
end

Capabilities

Capabilities are labels that define what types of processes a node is responsible for handling. They enable workload isolation and allow you to create specialized node types in your cluster.

Think of capabilities as tags that describe a node's role. For example:

  • :game - Game-related processes (game servers, matchmaking)
  • :chat - Chat/messaging processes (rooms, conversations)
  • :payment - Payment processing (transactions, billing)

When you invoke a process with a specific capability, Mesh automatically routes it to a node that has registered that capability. This allows you to:

  • Isolate workloads (separate game logic from payment processing)
  • Scale specific capabilities independently (add more game nodes without affecting chat)
  • Use specialized hardware for different workload types

Register Capabilities

Tell Mesh what this node can handle:

Mesh.register_capabilities([:game, :chat])

You can do this in your application start or in a dedicated worker:

defmodule MyApp.CapabilityRegistrar do
  use GenServer

  def start_link(_) do
    GenServer.start_link(__MODULE__, [])
  end

  def init(_) do
    # Register capabilities when the node starts
    Mesh.register_capabilities([:game, :chat])
    {:ok, nil}
  end
end

Routing

Mesh automatically routes process invocations to nodes that support the requested capability:

# This will be routed to a node with :game capability
{:ok, pid, _} = Mesh.call(%Mesh.Request{module: GameActor, id: "player_1", payload: payload, capability: :game})

# This will be routed to a node with :chat capability
{:ok, pid, _} = Mesh.call(%Mesh.Request{module: ChatActor, id: "room_1", payload: payload, capability: :chat})

Multi-Node Example

Node 1 - Game Server

# On node1@host
Mesh.register_capabilities([:game])

Node 2 - Chat Server

# On node2@host
Mesh.register_capabilities([:chat])

Node 3 - Universal

# On node3@host
Mesh.register_capabilities([:game, :chat, :payment])

Now processes will be automatically distributed:

  • Game processes go to node1 or node3
  • Chat processes go to node2 or node3
  • Payment processes only go to node3

Node Discovery Strategies

Mesh is agnostic about how nodes discover each other. Use libcluster strategies:

  • Gossip: Local network multicast
  • Kubernetes: K8s service discovery
  • EPMD: Static node list
  • DNS: DNS-based discovery

See libcluster documentation for more strategies.