Packages
macula
0.30.8
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
Current section
Files
src/macula_provider_selector.erl
%%%-------------------------------------------------------------------
%%% @doc
%%% Provider selection strategies for multi-provider RPC load balancing.
%%%
%%% Supports multiple strategies for choosing which provider to use
%%% when multiple providers advertise the same service.
%%%
%%% Strategies:
%%% - round_robin: Distribute calls evenly across providers
%%% - random: Random provider selection
%%% - first: Always use first provider (default/simple)
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(macula_provider_selector).
%% API exports
-export([
select_provider/2,
select_provider/3
]).
-export_type([
strategy/0,
provider_info/0,
selection_state/0
]).
%%%===================================================================
%%% Types
%%%===================================================================
-type strategy() :: round_robin | random | first.
%% Selection strategy for choosing a provider from multiple options.
-type provider_info() :: #{
node_id := binary(),
endpoint := binary(),
metadata := map(),
advertised_at => integer(),
ttl => pos_integer()
}.
%% Provider information returned from DHT.
-type selection_state() :: #{
strategy => strategy(),
counters => #{binary() => non_neg_integer()} % Service ID -> call counter for round-robin
}.
%% State maintained by the selector for stateful strategies.
%%%===================================================================
%%% API Functions
%%%===================================================================
%% @doc Select a provider from a list using the default strategy (random).
%%
%% Returns the selected provider or error if list is empty.
-spec select_provider([provider_info()], selection_state()) ->
{ok, provider_info(), selection_state()} | {error, no_providers}.
select_provider(Providers, State) ->
select_provider(Providers, random, State).
%% @doc Select a provider from a list using a specific strategy.
%%
%% Strategies:
%% - first: Always select the first provider (simple, no state)
%% - random: Randomly select a provider
%% - round_robin: Distribute calls evenly using a counter
%%
%% Examples:
%% ```
%% %% Random selection
%% State = new_state(random),
%% {ok, Provider, State2} = select_provider(Providers, random, State).
%%
%% %% Round-robin selection
%% State = new_state(round_robin),
%% {ok, P1, State2} = select_provider(Providers, round_robin, State),
%% {ok, P2, State3} = select_provider(Providers, round_robin, State2),
%% %% P1 and P2 will be different providers (if multiple available)
%% '''
-spec select_provider([provider_info()], strategy(), selection_state()) ->
{ok, provider_info(), selection_state()} | {error, no_providers}.
select_provider([], _Strategy, _State) ->
{error, no_providers};
select_provider(Providers, first, State) when is_list(Providers) ->
%% Simple: always pick first provider
[Provider | _] = Providers,
{ok, Provider, State};
select_provider(Providers, random, State) when is_list(Providers) ->
%% Random selection
Index = rand:uniform(length(Providers)),
Provider = lists:nth(Index, Providers),
{ok, Provider, State};
select_provider(Providers, round_robin, State) when is_list(Providers) ->
%% Round-robin: need service_id to track counter
%% Extract service_id from state or use a default key
ServiceId = maps:get(current_service_id, State, <<"default">>),
%% Get current counter for this service
Counters = maps:get(counters, State, #{}),
Counter = maps:get(ServiceId, Counters, 0),
%% Select provider based on counter
Index = (Counter rem length(Providers)) + 1,
Provider = lists:nth(Index, Providers),
%% Update counter
NewCounter = Counter + 1,
NewCounters = Counters#{ServiceId => NewCounter},
NewState = State#{counters => NewCounters},
{ok, Provider, NewState}.
%%%===================================================================
%%% Helper Functions
%%%===================================================================
%% Note: State management for round-robin is handled by macula_connection.erl
%% which maintains the counters in its #state.provider_selector_state field.