Skip to content

Commit

Permalink
add API to list voters
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Valiushko committed Jan 23, 2024
1 parent 90078d4 commit 1e95870
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 4 deletions.
40 changes: 40 additions & 0 deletions src/ra.erl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
%% queries
members/1,
members/2,
voters/1,
voters/2,
initial_members/1,
initial_members/2,
local_query/2,
Expand Down Expand Up @@ -1037,6 +1039,44 @@ members({local, ServerId}, Timeout) ->
members(ServerId, Timeout) ->
ra_server_proc:state_query(ServerId, members, Timeout).

%% @doc Returns a list of cluster voters
%%
%% Except if `{local, ServerId}' is passed, the query is sent to the specified
%% server which may redirect it to the leader if it is a follower. It may
%% timeout if there is currently no leader (i.e. an election is in progress).
%%
%% With `{local, ServerId}', the query is always handled by the specified
%% server. It means the returned list might be out-of-date compared to what the
%% leader would have returned.
%%
%% @param ServerId the Ra server(s) to send the query to
%% @end
-spec voters(ra_server_id() | [ra_server_id()] | {local, ra_server_id()}) ->
ra_server_proc:ra_leader_call_ret([ra_server_id()]).
voters(ServerId) ->
voters(ServerId, ?DEFAULT_TIMEOUT).

%% @doc Returns a list of cluster voters
%%
%% Except if `{local, ServerId}' is passed, the query is sent to the specified
%% server which may redirect it to the leader if it is a follower. It may
%% timeout if there is currently no leader (i.e. an election is in progress).
%%
%% With `{local, ServerId}', the query is always handled by the specified
%% server. It means the returned list might be out-of-date compared to what the
%% leader would have returned.
%%
%% @param ServerId the Ra server(s) to send the query to
%% @param Timeout the timeout to use
%% @end
-spec voters(ra_server_id() | [ra_server_id()] | {local, ra_server_id()},
timeout()) ->
ra_server_proc:ra_leader_call_ret([ra_server_id()]).
voters({local, ServerId}, Timeout) ->
ra_server_proc:local_state_query(ServerId, voters, Timeout);
voters(ServerId, Timeout) ->
ra_server_proc:state_query(ServerId, voters, Timeout).

%% @doc Returns a list of initial (seed) cluster members.
%%
%% This allows Ra-based systems with dynamic cluster membership
Expand Down
2 changes: 1 addition & 1 deletion src/ra_server_proc.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1552,7 +1552,7 @@ do_state_query(voters, #{cluster := Cluster}) ->
end
end
end, [], Cluster),
Vs;
lists:sort(Vs);
do_state_query(members, #{cluster := Cluster}) ->
maps:keys(Cluster);
do_state_query(initial_members, #{log := Log}) ->
Expand Down
31 changes: 28 additions & 3 deletions test/ra_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ all_tests() ->
local_query_boom,
local_query_stale,
members,
voters_promotable,
voters_non_voter,
consistent_query,
consistent_query_after_restart,
consistent_query_minority,
Expand Down Expand Up @@ -537,6 +539,29 @@ members(Config) ->
{ok, Cluster, Leader} = ra:members(Leader),
terminate_cluster(Cluster).

voters_promotable(Config) ->
Name = ?config(test_name, Config),
[A, B] = Cluster = start_local_cluster(2, Name, add_machine()),
{ok, _, Leader} = ra:process_command(A, 9),
C = {ra_server:name(Name, "3"), node()},
ok = ra:start_server(default, Name, C, add_machine(), Cluster),
{ok, _, _} = ra:add_member(Leader, #{id => C, uid => <<"4">>, membership => promotable}),
{ok, [A, B, C], Leader} = ra:members(Leader),
{ok, [A, B], Leader} = ra:voters(Leader),
terminate_cluster([C | Cluster]).

voters_non_voter(Config) ->
Name = ?config(test_name, Config),
[A, B] = Cluster = start_local_cluster(2, Name, add_machine()),
{ok, _, Leader} = ra:process_command(A, 9),
C = {ra_server:name(Name, "3"), node()},
ok = ra:start_server(default, Name, C, add_machine(), Cluster),
{ok, _, _} = ra:add_member(Leader, #{id => C, uid => <<"4">>, membership => non_voter}),
{ok, [A, B, C], Leader} = ra:members(Leader),
{ok, [A, B], Leader} = ra:voters(Leader),
terminate_cluster([C | Cluster]).


consistent_query(Config) ->
[A, _, _] = Cluster = start_local_cluster(3, ?config(test_name, Config),
add_machine()),
Expand Down Expand Up @@ -1073,7 +1098,7 @@ voter_gets_promoted_consistent_leader(Config) ->
timer:sleep(100),
All = [N1, N2, N3],
% in server state
lists:map(fun(O) -> ?assertEqual(All, voters(O)) end, overviews(N1)),
lists:map(fun(O) -> ?assertEqual(All, filter_voters(O)) end, overviews(N1)),
% in ets
#{servers := Servers} = ra:overview(?SYS),
lists:map(fun({Name, _}) -> #{Name := #{membership := voter}} = Servers end, All),
Expand All @@ -1098,7 +1123,7 @@ voter_gets_promoted_new_leader(Config) ->
timer:sleep(100),
All = [N1, N2, N3],
% in server state
lists:map(fun(O) -> ?assertEqual(All, voters(O)) end, overviews(N1)),
lists:map(fun(O) -> ?assertEqual(All, filter_voters(O)) end, overviews(N1)),
% in ets
#{servers := Servers} = ra:overview(?SYS),
lists:map(fun({Name, _}) -> #{Name := #{membership := voter}} = Servers end, All),
Expand Down Expand Up @@ -1217,7 +1242,7 @@ overviews(Node) ->
{ok, Members, _From} = ra:members(Node),
[ra:member_overview(P) || {_, _} = P <- Members].

voters({ok, #{cluster := Peers}, _} = _Overview) ->
filter_voters({ok, #{cluster := Peers}, _} = _Overview) ->
[Id || {Id, Status} <- maps:to_list(Peers), maps:get(membership, Status, voter) == voter].

%% machine impl
Expand Down

0 comments on commit 1e95870

Please sign in to comment.