Malla (malla v0.0.1-rc.1)

Copy Markdown View Source

Malla is a comprehensive framework that simplifies the development of distributed services through a plugin-based architecture with compile-time callback chaining, automatic service discovery across nodes, and minimal "magic" to keep systems understandable.

See the Introduction guide for a general overview.

API Overview

Service Management

Callback Invocation

  • local/2 - Invoke local callback with service_id from process dictionary.
  • local/3 - Invoke local callback.
  • remote/3 - Invoke remote callback.
  • remote/4 - Invoke remote callback with options.
  • call/1 - Macro for syntactic sugar to invoke remote callbacks.
  • call/2 - Macro for syntactic sugar to invoke remote callbacks with options.

Utilities

  • metric/4 - Record metric with auto-injected metadata.
  • event/2 - Generate event with service context.
  • authorize/3 - Authorization callback.

See Also

Summary

Functions

Utility function to authorize a request.

Convenient macro to make remote calls more friendly.

Convenient macro to make remote calls more friendly.

Gets the current service ID from the process dictionary, or nil if not defined.

Tries to extract the service ID from the :service_id key in a map or list, or, if not found, from the process dictionary. See get_service_id/0.

Tries to get the service ID from the process dictionary, or raises Malla.ServiceIdMissing. See get_service_id/0.

Tries to extract the service ID from the :service_id key in a map or list, or, if not found, from the process dictionary. If not found, it raises Malla.ServiceIdMissing. See get_service_id/0.

Returns metadata about a service ID. Cached for fast access.

Returns the string version of a service ID. Cached for fast access.

Invokes a service callback locally at this node. The service ID must be present in the process dictionary. See local/3.

Invokes a service callback locally at this node. Service does not need to be running, since this simply

Puts a service ID into the process dictionary.

Calls a callback function defined at the home module of a local or remote service, using Malla.Node.call_cb/4. This could be a normal function defined with def or a callback function defined with defcb.

Types

authorize_opt()

@type authorize_opt() :: {:service_id, id()} | term()

class()

@type class() :: atom()

config()

@type config() :: list()

cont()

@type cont() ::
  :cont | {:cont, list()} | {:cont, any(), any()} | {:cont, any(), any(), any()}

id()

@type id() :: module()

metric_opt()

@type metric_opt() :: {:service_id, id()}

remote_opt()

@type remote_opt() ::
  {:timeout, pos_integer()}
  | {:sna_retries, pos_integer()}
  | {:excp_retries, pos_integer()}
  | {:retries_sleep_msec, pos_integer()}

service()

@type service() :: Malla.Service.t()

vsn()

@type vsn() :: String.t()

Functions

authorize(resource, scope, opts \\ [])

@spec authorize(term(), term(), [authorize_opt()]) ::
  boolean() | {boolean(), term()} | {:error, term()}

Utility function to authorize a request.

It will simply call Malla.Plugins.Base.malla_authorize/3. You must implement this callback in your service. By default it will return {:error, :auth_not_implemented}.

call(arg)

(macro)

Convenient macro to make remote calls more friendly.

It calls remote/4 with the module, function name, and arguments extracted from the given expression.

Examples

call Module.fun(:a, :b), timeout: 5000
# Translates to: remote(Module, :fun, [:a, :b], timeout: 5000)

call(arg, opts)

(macro)

Convenient macro to make remote calls more friendly.

It calls remote/3 with the module, function name, and arguments extracted from the given expression.

Examples

call Module.fun(:a, :b)
# Translates to: remote(Module, :fun, [:a, :b])

get_service_id()

@spec get_service_id() :: id() | nil

Gets the current service ID from the process dictionary, or nil if not defined.

On each callback call, called using local/3 or remote/4, the service ID is always inserted in the process dictionary.

get_service_id(map)

@spec get_service_id(list() | map()) :: id() | nil

Tries to extract the service ID from the :service_id key in a map or list, or, if not found, from the process dictionary. See get_service_id/0.

get_service_id!()

@spec get_service_id!() :: id()

Tries to get the service ID from the process dictionary, or raises Malla.ServiceIdMissing. See get_service_id/0.

get_service_id!(term)

@spec get_service_id!(Keyword.t() | map()) :: id()

Tries to extract the service ID from the :service_id key in a map or list, or, if not found, from the process dictionary. If not found, it raises Malla.ServiceIdMissing. See get_service_id/0.

get_service_meta(id)

@spec get_service_meta(id()) :: %{
  cluster: String.t(),
  node: String.t(),
  host: String.t(),
  service: String.t()
}

Returns metadata about a service ID. Cached for fast access.

  • cluster is extracted from :malla application's :malla_cluster environment variable.
  • node is the current Erlang node.
  • host is the first part of the node name.
  • service uses get_service_name/1.

get_service_name(str)

@spec get_service_name(nil | id() | String.t()) :: String.t()

Returns the string version of a service ID. Cached for fast access.

local(fun, args)

@spec local(atom(), list()) :: any()

Invokes a service callback locally at this node. The service ID must be present in the process dictionary. See local/3.

local(srv_id, fun, args)

@spec local(id(), atom(), list()) :: any()

Invokes a service callback locally at this node. Service does not need to be running, since this simply:

  • puts service ID into process dictionary.
  • calls Malla.Plugins.Base.service_cb_in/3, which, if not overridden, will ultimately call the indicated function.
  • sets back previous value in process dictionary, if any.

metric(class, value \\ 1, meta \\ %{}, opts \\ [])

@spec metric(atom() | [atom()], number() | map() | keyword(), map() | keyword(), [
  metric_opt()
]) :: :ok

Inserts a new metric value.

It calls :telemetry.execute/3 with the given class, value and meta.

  • class: Taken from the calling arg, but converted to a list if it is not already.
  • value: If it is a number, it is converted to %{value: <number>}. If it is a list, it is converted to a map.
  • meta: Merged with the service's metadata obtained from get_service_meta/1. The service ID must be in opts or the process dictionary.

put_service_id(id)

@spec put_service_id(id() | nil) :: id()

Puts a service ID into the process dictionary.

This is used to mark the current process as belonging to this service. Callbacks called for a module will have it already.

If id is nil or :undefined, the key is deleted.

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

@spec remote(id(), atom(), [any()], [remote_opt()]) :: any()

Calls a callback function defined at the home module of a local or remote service, using Malla.Node.call_cb/4. This could be a normal function defined with def or a callback function defined with defcb.

On the remote side, the process leader will be changed to :user so that IO responses are not sent back to the caller. It will set the correct service ID in the process dictionary and call the callback function Malla.Plugins.Base.service_cb_in/3, which, if not overridden, will ultimately call the indicated function.

  • If the {:error, malla_service_not_available} is returned, it means Malla.Node could not find any service to process the request. The call will be retried if sna_retries is defined (default value is 5). The sleep time between retries can be set with retries_sleep_msec, and it is 1000 by default.

    This is very convenient in situations when the remote service is not yet available, because it may be starting or our node could not yet discover the service.

  • If an exception is produced during the call, the call is retried only if excp_retries is defined. Otherwise, the error {:error, {:malla_rpc_error, <error_data>}} is returned. Default excp_retries is 0.

    Be very careful when activating these retries, since the request could have been processed partially on the remote side, and you may re-execute it.

    The default timeout is 15000 ms.

    You can instrument the call by overriding Malla.Plugins.Base.service_cb_in/3.