classy ¶Main interface module of Classy application.
Note: business releases can install hooks by setting
classy:setup_hooks application environment variable to a tuple
{Module, Function, Args}.
This MFA can contain calls to various classy:on_... functions.
-type node_set() :: ordsets:ordset(node()).
A set of nodes.
-type node_set_name() :: all |
up |
down |
connected |
disconnected |
term().
An arbitrary ID of a node set.
Predefined sets are:
allnames of all previously seen nodes that belong (or belonged, if the site is currently down) to the cluster members.
updownconnectedthere’s an Erlang distribution connection to the node hosting the site.
disconnectedthere’s no Erlang distribution connection to the node hosting the site, but site is not considered down.
-type run_level() :: stopped |
single |
cluster |
quorum.
See Run level
-type kick_intent() :: {join,
#{node := node(), cluster := cluster_id(),
join_intent := join_intent()}} |
kicked |
autoclean |
term().
Kick intent is an arbitrary term passed to classy:pre_kick/2, classy:on_kick_decided/2 and classy:on_leave/2 hooks.
pre_kick may use intent to make a decision leaving the cluster in certain cases,
while to on_leave this value is merely informational.
Classy itself uses the following intents:
{join, #{node => node(), cluster => cluster_id(), ...}}:
Site is leaving the cluster to immediately join a different one.
kicked:
Site detects that it got kicked from the cluster by a third party.
autoclean:
Site is kicked by the autoclean logic.
-type join_intent() :: term().
Join intent is an arbitrary term passed to classy:pre_join/2 and classy:post_join/2 hooks.
pre_join may match on intent to prevent join in certain cases,
while to post_join this value is merely informational.
Classy itself uses the following intents:
autocluster:
When join is triggered by autocluster.
-type cluster_info() :: #{infos := #{node() => info()},
bad_nodes := #{node() => _}}.
-type site_metadata() :: map().
-type info() :: #{cluster := cluster_id() | undefined,
site := site() | undefined,
last_update := classy_lib:unix_time_s() | undefined,
peers := #{site() => peer_info()}, atom() => _}.
-type peer_info() :: #{node := node() | undefined,
connected := boolean(),
last_update := classy_lib:unix_time_s()}.
-type site() :: binary().
Unique random persistent identifier of the site. See Site ID.
-type cluster_id() :: binary().
Unique random persistent identifier of the cluster. See Cluster ID.
-spec on_metadata_change(fun((cluster_id(), site(),
site_metadata()) -> _),
classy_hook:prio()) -> classy_hook:hook().
Register a hook that is called when metadata of a site (local or remote) changes.
-spec enrich_site_info(fun((info()) -> info()),
classy_hook:prio()) -> classy_hook:hook().
Register a hook that can add entries to the map returned by classy:info/0.
-spec run_level(fun((run_level(), run_level()) -> _),
classy_hook:prio()) -> classy_hook:hook().
Register a hook that is executed on change of the run level of the local site.
When run level increases, hooks with higher priority run earlier; when it decreases, hooks run in the reverse order.
WARNING: if the callback interacts with the OTP application controller
(e.g. it starts or stops an OTP application),
then stopping classy application using application:stop(classy) will lead to a deadlock.
Use classy:prep_stop() function to safely lower the run level and shut down classy.
-spec pre_autocluster(fun((cluster_info(),
Discovered) -> Discovered),
classy_hook:prio()) -> classy_hook:hook() when Discovered ::
[{cluster_id(),
[node()]}].
Register a hook that filters and ranks nodes for autocluster. It allows the business code to pick the most appropriate cluster for automatic join.
WARNING: this hook cannot have side effects.
-spec pre_autoclean(fun((Remote) -> ok | {error, _}),
classy_hook:prio()) -> classy_hook:hook() when Remote ::
site().
Register a hook that runs before autoclean finalizes the decision to kick a down site.
WARNING: this hook cannot have side effects.
-spec on_leave(fun((OldCluster, Local,
kick_intent()) -> _),
classy_hook:prio()) -> classy_hook:hook() when OldCluster ::
cluster_id(),
Local :: site().
Register a hook that is executed after the local site leaves a cluster. This hook can perform destructive actions associated with cleanup.
-spec on_kick_decided(fun((cluster_id(), Target,
kick_intent()) -> _),
classy_hook:prio()) -> classy_hook:hook() when Target ::
site().
Register a hook that is executed after classy:pre_kick/2 hooks allow the kick to proceed, but before the membership change is applied and before the site left the cluster.
This hook is executed only on the node that initiates the kick procedure. It can be used to trigger some side effects while the site is still part of the cluster.
WARNING: theoretically, kick procedure can be aborted after this hook fires, or in the middle of its execution. In this case kick procedure won’t be retried. Then side effects of this hook will be observed, but the site will stay in the cluster. As such, it’s not recommended to perform any destructive actions here.
Normally, such actions should be performed in classy:on_membership_change/2.
-spec pre_kick(fun((cluster_id(), Target,
kick_intent()) -> ok | {error, _}),
classy_hook:prio()) -> classy_hook:hook() when Target ::
site().
Register a hook that verifies whether or not a site can be kicked from the cluster. This hook runs on the node that initiates the kick.
WARNING: this hook cannot have side effects.
-spec post_join(fun((cluster_id(), Local, JoinedTo,
join_intent()) -> _),
classy_hook:prio()) -> classy_hook:hook() when Local ::
site(),
JoinedTo ::
node().
Register a hook that is executed after the local site joins a cluster.
It is guaranteed to be called at least once, and must be idempotent.
-spec pre_join(fun((cluster_id(), Remote, node(),
join_intent()) -> ok | {error, _}),
classy_hook:prio()) -> classy_hook:hook() when Remote ::
site().
Register a hook that is executed before the local node joins a different cluster.
WARNING: this hook should not have side effects. It should only check if it is ok to join.
-spec
on_node_classify(fun((site_metadata()) -> [node_set()]),
classy_hook:prio()) -> classy_hook:hook().
Register a hook that can place a site’s node into an arbitrary number of custom node sets, based on t:classy:info/0.
See classy:enrich_site_info/2, See classy:node_sets/0.
-spec on_peer_restart(fun((_Remote :: site(),
_NRestarts :: pos_integer()) -> _),
classy_hook:prio()) -> classy_hook:hook().
Register a hook that is executed when a peer restarts.
See node_hook_execution.
-spec on_peer_node_change(fun((_Remote :: site(),
_OldNode :: node(), _NewNode :: node()) -> _),
classy_hook:prio()) -> classy_hook:hook().
Register a hook that is executed when a peer site changes the Erlang node name.
See node_hook_execution.
-spec on_peer_liveness_change(fun((_Remote :: site(),
_IsAlive :: boolean()) -> _),
classy_hook:prio()) -> classy_hook:hook().
Register a hook that is executed when a site changes status for up to down or vice versa.
Note: this hook is different from classy:on_peer_connection_change/2, as care is taken to avoid firing it during a network partition.
The decision to consider a peer down comes either from the peer itself when it shuts down gracefully or from the quorum of other running peers.
See node_hook_execution.
-spec on_membership_change(fun((cluster_id(),
_Local :: site(), _Remote :: site(),
_IsMember :: boolean()) -> _),
classy_hook:prio()) -> classy_hook:hook().
Register a hook that is executed when a site joins or leaves a cluster.
Note: this hook can be executed multiple times if the local node is abruptly stopped while the hooks are running. If the remote site re-joins the cluster while the local was down, the hook may or may not run.
-spec on_peer_connection_change(Fun,
classy_hook:prio()) -> classy_hook:hook() when Fun ::
fun((Remote,
node(),
_IsConnected ::
boolean()) -> _),
Remote ::
site().
Register a hook that is executed when a site changes
status from connected (true) to disconnected (false) and vice versa.
Note: this hook runs in the classy main process. Hence it should avoid blocking it.
WARNING: status change to false is not indicative of the remote site being actually down.
This can happen during a network partition.
-spec on_create_site(fun((site()) -> _),
classy_hook:prio()) -> classy_hook:hook().
This callback is called once per site.
-spec on_create_cluster(fun((cluster_id(), Local) -> _),
classy_hook:prio()) -> classy_hook:hook() when Local ::
site().
This callback is executed once per cluster by the site that originally creates the cluster.
-spec on_prep_stop(fun((_Reason) -> _),
classy_hook:prio()) -> classy_hook:hook().
Register a hook that is executed before shutting down business applications.
It is called before classy application starts the shutdown procedure, as well when the site leaves a cluster or heals from a network partition.
-spec on_node_init(fun(() -> _),
classy_hook:prio()) -> classy_hook:hook().
Register a hook that is executed when the node (not the site) starts.
It is called before classy:the_site/0 and classy:the_cluster/0
are initialized,
and can be used to override the default cluster and site initialization logic.
Calculate how many nodes can be down while cluster still can maintain quorum.
-spec quorum(config |
running |
non_neg_integer()) -> pos_integer().
Calculate the number of nodes required for the quorum:
Integer:
any integer value
config:
Return value of ‘classy.quorum’ application environment variable
running:
Quorum among the running sites, not less than quorum(config)
-spec the_cluster() -> {ok, cluster_id()} | undefined.
Get ID of the cluster.
-spec the_site() -> {ok, site()} | undefined.
Get ID of the local site.
-spec run_level() -> run_level().
Get current run level.
NOTE: the value is updated before classy:run_level/2 hooks run.
-spec at_lower_level(run_level(),
fun(() -> any())) -> ok | {error, _}.
This function can be used to lower the run level of the system to the given value and run the specified function.
This function can be used to implement migrations that require business applications to be stopped.
Note: this function returns immediately after scheduling the action, but before the function is executed.
-spec node_sets() -> #{node_set_name() => node_set()}.
Return a map of all node sets.
-spec nodes(node_set_name()) -> [node()].
List peer nodes that belong to a node set.
Important to note: this function returns node names of peer sites. Random connected nodes, such as shells or classy sites that are not member of the current cluster, are excluded.
-spec sites(node_set_name()) -> [site()].
Get contents of a site set.
Note: argument is the same as for node sets.
-spec sites() -> [site()].
List IDs of peer sites.
-spec kick_node(node(), kick_intent()) -> ok |
{error, _}.
Translate node name to a site ID and kick it via classy:kick_site/2.
-spec kick_site(site(), kick_intent()) -> ok |
{error, _}.
Remove a site from the cluster. Target site can be local or remote: it is allowed for a site to kick itself from the cluster.
The kicked site creates an entirely new cluster_id(), and joins it as a singleton member.
Local site (one that initiates kick) runs the following hooks
with Intent equal to the value of the argument:
NOTE: the intent is not propagated across different sites.
If the target site is not the same as the local site,
then the target runs classy:on_leave/2 with pre-defined intent kicked.
-spec join_node(node(), join_intent()) -> ok |
{error, _}.
Join the local site to the cluster of a remote node.
This function allows a node to join a cluster by connecting to a known peer.
Arguments:
Note: while the majority of classy APIs work with site IDs, joining a cluster is always done via regular Erlang node name.
-spec site_prop_delete(_Key) -> ok | {error, _}.
Delete a site property.
-spec site_prop_lookup(_Key) -> list().
Lookup a site property.
-spec site_prop_set(_Key, _Val) -> ok | {error, _}.
Persistentley set a site property.
These properties survive all cluster changes, they don’t get cleaned automatically.
WARNING: The purpose of node globals is to aid with node migration activities, such as migrating to classy application or between major releases.
Do NOT use this feature for arbitrary application data,
use separate classy_tables instead.
-spec prep_stop(term()) -> ok.
This function can be called before the Erlang node shuts down.
It gracefully lowers the run level to stopped, without blocking the application controller.
This is helpful if changing the run level involves stopping or starting OTP applications.
-spec prep_stop() -> ok.
Equivalent to prep_stop(shutdown).
-spec node_of_site(site(), boolean()) -> {ok, node()} |
{error,
{unknown_node | disconnected,
site()}}.
Locate a node that is currently hosting a site.
If OnlyConnected flag is set,
undefined is returned when the site is locally unreachable
(even if its node is otherwise known).
-spec node_to_site() -> {ok, #{node() => site()}} |
{error, not_in_cluster}.
Return a mapping from known node names to site IDs.
-spec n_restarts(site()) -> {ok, non_neg_integer()} |
undefined.
Get cached value of the number of restarts of a remote site.
Note: for the local site, please call classy:n_restarts/0, as values returned by this function may be out-of-date.
-spec n_restarts() -> non_neg_integer() | undefined.
Return the total number of times the site has been restarted. A fresh node starts with this value = 0.
This value increases when the Erlang VM itself restarts,
or classy application goes from stopped to single run level.
NOTE: This counter doesn’t get reset when node joins or leaves the cluster, it increases instead.
-spec get_meta(classy:site()) -> {ok, info()} |
undefined.
Get cached site metadata for a remote site.
NOTE: This function works even if the site is down.
See classy_site_metadata:set/2, classy_site_metadata:delete/1, classy_site_metadata:lookup/1.
-spec info([node()]) -> cluster_info().
Gather classy:info/0 from a set of nodes.
The nodes don’t have to be in the same cluster.
WARNING: as a side effect of calling this function,
the current node will establish Erlang distribution connection to all nodes in the list.
While this won’t affect code using classy:nodes(connected) API,
it may confuse code using plain erlang:nodes().
-spec info() -> info().
Provide general information about the local node.