Packages

macula

0.7.2
7.0.0 6.0.0 5.2.2 5.2.1 5.2.0 5.1.0 5.0.0 4.8.0 4.7.1 4.7.0 4.6.0 4.5.0 4.4.10 4.4.9 4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.1 4.3.0 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.1 4.1.0 4.0.0 3.16.0 3.15.3 3.15.2 3.15.1 3.14.0 3.13.0 3.12.1 3.12.0 3.11.1 3.11.0 3.10.3 3.10.2 3.10.1 3.9.0 3.8.0 3.7.0 3.5.0 3.4.0 3.3.0 3.2.0 3.1.0 3.0.0 2.1.1 2.1.0 2.0.0 1.5.2 1.5.1 1.4.30 1.4.29 1.4.28 1.4.27 1.4.26 1.4.25 1.4.24 1.4.23 1.4.22 1.4.21 1.4.20 1.4.19 1.4.18 1.4.17 1.4.16 1.4.15 1.4.14 1.4.13 1.4.11 1.4.10 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.1 1.3.0 1.2.0 1.1.0 1.0.10 1.0.9 1.0.8 1.0.7 1.0.6 1.0.5 1.0.4 1.0.3 1.0.2 1.0.1 1.0.0 0.48.6 0.48.5 0.48.4 0.48.3 0.48.2 0.48.1 0.48.0 0.47.1 0.47.0 0.46.3 0.46.1 0.46.0 0.45.3 0.45.2 0.45.1 0.45.0 0.44.2 0.44.1 0.44.0 0.43.3 0.43.2 0.43.1 0.43.0 0.42.9 0.42.8 0.42.7 0.42.6 0.42.5 0.42.4 0.42.3 0.42.2 0.42.1 0.42.0 0.41.1 0.41.0 0.40.1 0.40.0 0.39.9 0.39.8 0.39.7 0.39.6 0.39.5 0.39.4 0.39.3 0.39.2 0.39.1 0.39.0 0.38.8 0.38.7 0.38.6 0.38.5 0.38.4 0.38.3 0.38.2 0.38.1 0.38.0 0.37.7 0.37.6 0.37.5 0.37.4 0.37.3 0.37.2 0.37.1 0.37.0 0.36.6 0.36.5 0.36.4 0.36.3 0.36.2 0.36.1 0.36.0 0.35.4 0.35.3 0.35.2 0.35.1 0.35.0 0.34.1 0.34.0 0.33.1 0.33.0 0.32.5 0.32.4 0.32.3 0.32.2 0.32.1 0.32.0 0.31.9 0.31.8 0.31.7 0.31.6 0.31.5 0.31.4 0.31.3 0.31.2 0.31.1 0.31.0 0.30.10 0.30.9 0.30.8 0.30.7 0.30.6 0.30.5 0.30.4 0.30.3 0.30.2 0.30.1 0.30.0 0.29.0 0.28.3 0.28.2 0.28.1 0.28.0 0.27.1 0.27.0 0.26.1 0.26.0 0.25.6 0.25.5 0.25.4 0.25.3 0.25.2 0.25.1 0.25.0 0.24.6 0.24.5 0.24.4 0.24.3 0.24.2 0.24.1 0.24.0 0.23.3 0.23.2 0.23.1 0.23.0 0.22.12 0.22.11 0.22.10 0.22.9 0.22.8 0.22.7 0.22.6 0.22.5 0.22.4 0.22.3 0.22.2 0.22.1 0.22.0 0.21.7 0.21.6 0.21.5 0.21.4 0.21.2 0.21.1 0.21.0 0.20.25 0.20.24 0.20.23 0.20.22 0.20.21 0.20.20 0.20.19 0.20.18 0.20.17 0.20.16 0.20.15 0.20.14 0.20.13 0.20.12 0.20.11 0.20.10 0.20.9 0.20.8 0.20.7 0.20.6 0.20.5 0.20.3 0.20.2 0.20.1 0.20.0 0.19.2 0.19.1 0.19.0 0.18.1 0.18.0 0.17.4 0.17.3 0.17.2 0.17.1 0.17.0 0.16.6 0.16.5 0.16.4 0.16.3 0.16.2 0.16.1 0.16.0 0.15.1 0.15.0 0.14.3 0.14.2 0.14.1 0.14.0 0.12.6 0.12.5 0.12.3 0.11.3 0.10.2 0.10.1 0.10.0 0.9.2 0.9.1 0.9.0 0.8.25 0.8.24 0.8.23 0.8.22 0.8.21 0.8.20 0.8.19 0.8.18 0.8.17 0.8.16 0.8.15 0.8.14 0.8.13 0.8.12 0.8.11 0.8.10 0.8.9 0.8.8 0.8.7 0.8.6 0.8.5 0.8.4 0.8.3 0.8.2 0.8.1 0.8.0 0.7.30 0.7.29 0.7.28 0.7.27 0.7.26 0.7.25 0.7.24 0.7.23 0.7.22 0.7.21 0.7.20 0.7.19 0.7.18 0.7.17 0.7.16 0.7.15 0.7.14 0.7.13 0.7.12 0.7.11 0.7.10 0.7.9 0.7.8 0.7.7 0.7.6 0.7.5 0.7.4 0.7.3 0.7.2 0.7.1 0.7.0 0.6.7 0.6.6 0.6.5 0.6.4 0.6.3 0.6.2 0.6.1 0.6.0 0.5.0 0.4.4 0.4.3 0.4.2 0.4.1 0.4.0 0.3.4 0.3.3 0.3.2 0.3.1

Macula HTTP/3 Mesh SDK — connect, subscribe, publish, call, advertise

Current section

Files

Jump to
macula src macula_rpc_router.erl
Raw

src/macula_rpc_router.erl

%%%-------------------------------------------------------------------
%%% @doc
%%% RPC call routing strategies.
%%% Selects which provider to use for a call (local or remote).
%%% @end
%%%-------------------------------------------------------------------
-module(macula_rpc_router).
%% API
-export([
select_provider/3,
select_provider_stateful/3,
select_provider_closest/3,
select_local/1,
select_remote_random/1,
new_state/1
]).
%% Types
-type strategy() :: local_first | round_robin | random | closest.
-type registration() :: macula_rpc_registry:registration().
-type provider_info() :: macula_rpc_discovery:provider_info().
-type router_state() :: #{
strategy := strategy(),
round_robin_index := non_neg_integer()
}.
-export_type([strategy/0, router_state/0]).
%%%===================================================================
%%% API Functions
%%%===================================================================
%% @doc Create new router state.
-spec new_state(strategy()) -> router_state().
new_state(Strategy) ->
#{
strategy => Strategy,
round_robin_index => 0
}.
%% @doc Select provider using stateless strategy.
%% For local_first, random, and closest strategies.
-spec select_provider(strategy(), [registration()], [provider_info()]) ->
{local, registration()} | {remote, provider_info()} | {error, no_provider}.
select_provider(local_first, LocalHandlers, RemoteProviders) ->
case select_local(LocalHandlers) of
{ok, Handler} -> {local, Handler};
not_found ->
case select_remote_random(RemoteProviders) of
{ok, Provider} -> {remote, Provider};
not_found -> {error, no_provider}
end
end;
select_provider(random, _LocalHandlers, RemoteProviders) ->
case select_remote_random(RemoteProviders) of
{ok, Provider} -> {remote, Provider};
not_found -> {error, no_provider}
end;
select_provider(round_robin, _LocalHandlers, _RemoteProviders) ->
%% Round robin requires state, use select_provider_stateful instead
{error, use_stateful_api};
select_provider(closest, _LocalHandlers, _RemoteProviders) ->
%% Closest requires local node ID, use select_provider_closest instead
{error, use_closest_api}.
%% @doc Select provider using stateful strategy (for round_robin).
-spec select_provider_stateful(router_state(), [registration()], [provider_info()]) ->
{{local, registration()} | {remote, provider_info()} | {error, no_provider}, router_state()}.
select_provider_stateful(#{strategy := round_robin, round_robin_index := Index} = State, _LocalHandlers, RemoteProviders) ->
case RemoteProviders of
[] ->
{{error, no_provider}, State};
_ ->
%% Select provider at current index
ProviderIndex = Index rem length(RemoteProviders),
Provider = lists:nth(ProviderIndex + 1, RemoteProviders),
%% Increment index for next call
NewState = State#{round_robin_index => Index + 1},
{{remote, Provider}, NewState}
end;
select_provider_stateful(#{strategy := Strategy} = State, LocalHandlers, RemoteProviders) ->
%% For non-stateful strategies, use stateless version
Result = select_provider(Strategy, LocalHandlers, RemoteProviders),
{Result, State}.
%% @doc Select provider using closest strategy (requires local node ID).
-spec select_provider_closest(binary(), [registration()], [provider_info()]) ->
{local, registration()} | {remote, provider_info()} | {error, no_provider}.
select_provider_closest(_LocalNodeId, LocalHandlers, RemoteProviders) ->
case select_local(LocalHandlers) of
{ok, Handler} -> {local, Handler};
not_found ->
case find_closest_provider(_LocalNodeId, RemoteProviders) of
{ok, Provider} -> {remote, Provider};
not_found -> {error, no_provider}
end
end.
%% @doc Select local handler (returns first one).
-spec select_local([registration()]) -> {ok, registration()} | not_found.
select_local([]) ->
not_found;
select_local([Handler | _Rest]) ->
{ok, Handler}.
%% @doc Select random remote provider.
-spec select_remote_random([provider_info()]) -> {ok, provider_info()} | not_found.
select_remote_random([]) ->
not_found;
select_remote_random(Providers) ->
Index = rand:uniform(length(Providers)),
Provider = lists:nth(Index, Providers),
{ok, Provider}.
%%%===================================================================
%%% Internal Functions
%%%===================================================================
%% @doc Find provider with smallest XOR distance to local node.
-spec find_closest_provider(binary(), [provider_info()]) -> {ok, provider_info()} | not_found.
find_closest_provider(_LocalNodeId, []) ->
not_found;
find_closest_provider(LocalNodeId, Providers) ->
%% Calculate XOR distances
ProvidersWithDistance = lists:map(
fun(Provider) ->
RemoteNodeId = maps:get(node_id, Provider),
Distance = xor_distance(LocalNodeId, RemoteNodeId),
{Distance, Provider}
end,
Providers
),
%% Sort by distance (ascending)
Sorted = lists:sort(ProvidersWithDistance),
%% Return closest
{_Distance, Closest} = hd(Sorted),
{ok, Closest}.
%% @doc Calculate XOR distance between two node IDs.
-spec xor_distance(binary(), binary()) -> non_neg_integer().
xor_distance(A, B) ->
<<AInt:256>> = A,
<<BInt:256>> = B,
AInt bxor BInt.