Malla.Node (malla v0.0.1-rc.1)

Copy Markdown View Source

Manages distributed Malla services across a cluster of nodes.

Malla.Node is a GenServer that monitors services, performs health checks, and provides tools for remote procedure calls (RPC). It is the core of Malla's service discovery mechanism.

When a Malla.Service is defined with global: true, it registers with Malla.Node, making it discoverable by other nodes in the cluster.

Key features include:

  • Service Discovery: Tracks which services are running on which nodes.
  • RPC Calls: Provides call/4, call_cb/4 and call_cb_all/4 to invoke functions on remote services with automatic failover.
  • Health Checks: Periodically re-checks all services on the network.
  • Virtual Modules: Can dynamically generate proxy modules to make remote calls transparent.

For more information on how services are discovered and called, see the guides:

Summary

Functions

Launches a call to a any module and function at a remote node.

Launches a request to call a callback defined on a local or remote service.

Similar to call_cb/4 but sends the call to all nodes implementing the requested service.

Gets all nodes implementing a specific service if it is in running state at that node, along with metadata.

Gets all nodes implementing a specific service if it is in running state at that node.

Retrieves all detected service ids, their corresponding pids and metadata.

Precompiles stub modules for services defined in application configuration.

Waits for all services in the list to become available by checking get_nodes/1.

Types

call_opt()

@type call_opt() ::
  {:timeout, timeout()} | {:service_id, Malla.id()} | {:nodes, [node()]}

call_result()

@type call_result() ::
  {:error, :malla_service_not_available | {:malla_rpc, {term(), String.t()}}}
  | term()

instance_status()

@type instance_status() :: {node(), metadata :: service_info()}

service_info()

@type service_info() :: Malla.Service.service_info()

service_info_message()

@type service_info_message() :: %{id: atom(), pid: pid(), meta: service_info()}

Functions

call(mod, fun, args, opts \\ [])

@spec call(module(), atom(), list(), [call_opt()]) :: call_result()

Launches a call to a any module and function at a remote node.

A node list is obtained from nodes parameter, or, if not present, a service's id must be obtained from service_id parameter or must be present in process dictionary (see Malla.put_service_id/1). Then we will use get_nodes/1 to find nodes running this service.

First node in the list will be called and, only if it returns {:error, :malla_service_not_available} from remote, the next one is tried. If the local node is running the service, it will be called first.

Returns {:error, :malla_service_not_available} if we exhaust the instances and none is available. Returns {:error, {:malla_rpc, {term, text}}} if an exception is produced remotely or in :erpc.call/5.

If :timeout is set to zero, call will be asynchronous.

call_cb(srv_id, fun, args, opts \\ [])

@spec call_cb(Malla.id(), atom(), list(), [call_opt()]) :: call_result()

Launches a request to call a callback defined on a local or remote service.

It works by using call/4 but calling special function Malla.Service.Interface.malla_cb_in/3, that is always defined in remote services.

This function will change process group (so that IO is not redirected to caller), set service id and call malla callback Malla.Plugins.Base.service_cb_in/3; this, by default, will simply call the requested callback.

If :timeout is set to zero, call will be asynchronous.

call_cb_all(srv_id, fun, args, opts \\ [])

@spec call_cb_all(Malla.id(), atom(), list(), [call_opt()]) :: [call_result()]

Similar to call_cb/4 but sends the call to all nodes implementing the requested service.

It returns all responses from all nodes. If :timeout is set to zero, calls will be asynchronous.

get_instances(srv_id)

@spec get_instances(Malla.id()) :: [instance_status()]

Gets all nodes implementing a specific service if it is in running state at that node, along with metadata.

If the local node is running the service, it will be first in the list. The rest will shuffle every service recheck for load-balancing purposes.

get_nodes(srv_id)

@spec get_nodes(Malla.id()) :: [node()]

Gets all nodes implementing a specific service if it is in running state at that node.

If the local node is running the service, it will be first in the list. The rest will shuffle every service recheck for load-balancing purposes.

get_services()

@spec get_services() :: %{
  services_info: %{required(pid()) => {Malla.id(), [{:meta, service_info()}]}},
  services_id: [Malla.id()]
}

Retrieves all detected service ids, their corresponding pids and metadata.

precompile_stubs()

@spec precompile_stubs() :: :ok

Precompiles stub modules for services defined in application configuration.

Reads the :precompile config key from :malla application and creates stub modules for each service. These stubs will return {:error, :malla_service_not_available} until the actual service is discovered.

Configuration format:

config :malla,
  precompile: [
    MyService: [callback1: 0, callback2: 3]
  ]

wait_for_services(list, opts \\ [])

@spec wait_for_services(
  [Malla.id()],
  keyword()
) :: :ok | :timeout

Waits for all services in the list to become available by checking get_nodes/1.

Options can include :retries (default 10) for number of attempts, with 1-second sleeps between retries.

Returns :ok if all services are available, :timeout otherwise.