Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions doc/classy.texi
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,14 @@ The document was typeset with
Site is fully connected and operational.
@end enumerate

Unless the node is stopped abruptly
(@code{erlang:halt}, @code{kill}, power loss),
run levels always advance and retard in sequence.

Business applications can track the run level via @ref{classy:run_level/2} hook.
This hook can, for example,
activate or deactivate some business applications.

@node Network Partition
@section Network Partitions

Expand All @@ -123,14 +131,19 @@ The document was typeset with
Peer discovery is done with a help of several modules implementing
@ref{api/classy/classy_discovery_strategy, discovery_strategy} behavior.

Active strategy is selected by @ref{discovery_strategy} OTP application environment variable.
The strategy is configured via @ref{discovery_strategy} OTP application environment variable.
This variable has a form of 2-tuple with the following elements:

@enumerate
@item Strategy
@item Strategy-specific parameters
@item Discovery method, such as @code{static}
@item Method-specific parameters
@end enumerate

New methods can be registered using @ref{classy_discovery_strategy:hook/2} function.

Additionally, there is a hook that allows to filter and prioritize discovered nodes:
@ref{classy:pre_autocluster/2}.

Autocluster logic runs only on singleton sites.
It is inactive on sites that already have peers (up or down).

Expand Down
14 changes: 3 additions & 11 deletions src/classy_autocluster.erl
Original file line number Diff line number Diff line change
Expand Up @@ -245,20 +245,12 @@ wakeup(After, S = #s{t = T0}) ->

with_strategy(Fun) ->
case classy_discovery_strategy:get() of
{manual, _} ->
ignore;
{singleton, _} ->
ignore;
{Module, Options} ->
Fun(Module, Options);
undefined ->
Comment thread
ieQu1 marked this conversation as resolved.
ignore;
{Strategy, Options} ->
Fun(strategy_module(Strategy), Options)
ignore
end.

-spec strategy_module(atom()) -> module().
strategy_module(Strategy) ->
list_to_atom("classy_discovery_" ++ atom_to_list(Strategy)).

-spec discovery_interval() -> pos_integer().
discovery_interval() ->
application:get_env(classy, discovery_interval, 5_000).
Expand Down
17 changes: 10 additions & 7 deletions src/classy_builtin_hooks.erl
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,16 @@ log_run_level(From, To) ->
, to => To
}).

log_peer_connection_change(_Cluster, _Local, Remote, Node, true) ->
?tp(notice, classy_peer_connected,
#{ remote => Remote
, node => Node
});
log_peer_connection_change(_Cluster, _Local, Remote, Node, false) ->
?tp(notice, classy_peer_disconnected,
log_peer_connection_change(_Cluster, Local, Remote, Node, ConnStatus) ->
Kind = case ConnStatus of
true -> classy_peer_connected;
false -> classy_peer_disconnected
end,
Level = case Remote of
Local -> debug;
_ -> notice
end,
?tp(Level, Kind,
#{ remote => Remote
, node => Node
}).
Expand Down
58 changes: 52 additions & 6 deletions src/classy_discovery_strategy.erl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
This module defines a behavior of discovery strategy.
""".

-export([hook/2]).
-export([get/0, discover/2, lock/2, unlock/2, register/2, unregister/2]).

-export_type([options/0, t/0]).
Expand All @@ -43,15 +44,40 @@ This module defines a behavior of discovery strategy.

-callback unregister(options()) -> ok | ignore | {error, term()}.

-optional_callbacks([lock/1, unlock/1, register/1, unregister/1]).

-define(hook, ?MODULE).

%%================================================================================
%% API functions
%%================================================================================

-doc """
Read value of @ref{discovery_strategy} environment variable (with default).
Register a discovery strategy.

Callbacks registered here match on the @ref{discovery_strategy} configuration
and return callback module implementing @code{classy_discovery_strategy} behavior.

The first hook that returns @code{@{ok, Module@}} wins
and handles all the callbacks for the next discovery cycle.
""".
-spec hook(fun(({atom(), options()}) -> {ok, module()} | undefined), classy_hook:prio()) -> classy_hook:hook().
hook(Fun, Prio) when is_function(Fun, 1), is_number(Prio) ->
classy_hook:insert(?hook, Fun, Prio).

-doc """
Read @ref{discovery_strategy} environment variable and decide which strategy to use.
""".
-spec get() -> t() | undefined.
get() ->
application:get_env(classy, discovery_strategy, {manual, []}).
Conf = application:get_env(classy, discovery_strategy, {manual, []}),
case classy_hook:first_match(?hook, [Conf]) of
{ok, Module} ->
{_Method, Options} = Conf,
{Module, Options};
undefined ->
undefined
end.
Comment on lines 55 to +80

%%================================================================================
%% Internal exports
Expand All @@ -65,22 +91,42 @@ discover(Mod, Options) ->
-doc false.
-spec lock(module(), options()) -> ok | ignore | {error, term()}.
lock(Mod, Options) ->
safe_call(Mod, ?FUNCTION_NAME, Options).
case erlang:function_exported(Mod, ?FUNCTION_NAME, 1) of
true ->
safe_call(Mod, ?FUNCTION_NAME, Options);
false ->
ok
end.

-doc false.
-spec unlock(module(), options()) -> ok | ignore | {error, term()}.
unlock(Mod, Options) ->
safe_call(Mod, ?FUNCTION_NAME, Options).
case erlang:function_exported(Mod, ?FUNCTION_NAME, 1) of
true ->
safe_call(Mod, ?FUNCTION_NAME, Options);
false ->
ok
end.

-doc false.
-spec register(module(), options()) -> ok | ignore | {error, term()}.
register(Mod, Options) ->
safe_call(Mod, ?FUNCTION_NAME, Options).
case erlang:function_exported(Mod, ?FUNCTION_NAME, 1) of
true ->
safe_call(Mod, ?FUNCTION_NAME, Options);
false ->
ok
end.

-doc false.
-spec unregister(module(), options()) -> ok | ignore | {error, term()}.
unregister(Mod, Options) ->
safe_call(Mod, ?FUNCTION_NAME, Options).
case erlang:function_exported(Mod, ?FUNCTION_NAME, 1) of
true ->
safe_call(Mod, ?FUNCTION_NAME, Options);
false ->
ok
end.

%%================================================================================
%% Internal functions
Expand Down
6 changes: 5 additions & 1 deletion src/classy_hook.erl
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ init() ->
classy:on_membership_change(fun classy_builtin_hooks:log_membership_change/4, 100),
classy:run_level(fun classy_builtin_hooks:log_run_level/2, -100),
classy:on_peer_connection_change(fun classy_builtin_hooks:log_peer_connection_change/5, 100),
%% Discovery strategies:
classy_discovery_static:hook(),
classy_discovery_dns:hook(),
classy_discovery_k8s:hook(),
classy_discovery_etcd:hook(),
%% User initialization:
case application:get_env(classy, setup_hooks) of
{ok, {Mod, Func, Args}} ->
Expand All @@ -74,7 +79,6 @@ init() ->
ok
end.

-doc false.
-spec insert(hookpoint(), fun(), prio()) -> hook().
insert(Hookpoint, Hook, Prio) when is_atom(Hookpoint), is_integer(Prio), is_function(Hook) ->
Key = {Hookpoint, -Prio, Hook},
Expand Down
1 change: 1 addition & 0 deletions src/classy_internal.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
-define(classy_vote_part_perform_action, classy_vote_part_perform_action).
-define(classy_vote_flow_start, classy_vote_flow_start).
-define(classy_vote_part_established, classy_vote_part_established).
-define(classy_vote_part_recv_outcome, classy_vote_part_recv_outcome).
-define(classy_vote_alloc_id, classy_vote_alloc_id).
-define(classy_vote_part_recv, classy_vote_part_recv).
-define(classy_vote_part_send_vote, classy_vote_part_send_vote).
Expand Down
Loading
Loading