Skip to content

Commit 2a66f2b

Browse files
authored
Merge pull request #530 from anhanhnguyen/force-shrink-to-non-voter
Allow force shrink to non-voter member
2 parents 0fc773e + 76572df commit 2a66f2b

File tree

2 files changed

+59
-6
lines changed

2 files changed

+59
-6
lines changed

src/ra_server.erl

+4-2
Original file line numberDiff line numberDiff line change
@@ -1495,13 +1495,15 @@ handle_follower({register_external_log_reader, Pid}, #{log := Log0} = State) ->
14951495
{follower, State#{log => Log}, Effs};
14961496
handle_follower(force_member_change,
14971497
#{cfg := #cfg{id = Id,
1498+
uid = Uid,
14981499
log_id = LogId}} = State0) ->
1499-
Cluster = #{Id => new_peer()},
1500+
Cluster = #{Id => new_peer_with(#{voter_status => #{uid => Uid,
1501+
membership => voter}})},
15001502
?WARN("~ts: Forcing cluster change. New cluster ~w",
15011503
[LogId, Cluster]),
15021504
{ok, _, _, State, Effects} =
15031505
append_cluster_change(Cluster, undefined, no_reply, State0, []),
1504-
call_for_election(pre_vote, State, [{reply, ok} | Effects]);
1506+
call_for_election(pre_vote, State#{membership => voter}, [{reply, ok} | Effects]);
15051507
handle_follower(#info_rpc{term = Term} = Msg,
15061508
#{current_term := CurTerm} = State)
15071509
when CurTerm < Term ->

test/ra_2_SUITE.erl

+55-4
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ all_tests() ->
4848
add_member_without_quorum,
4949
force_start_follower_as_single_member,
5050
force_start_follower_as_single_member_nonvoter,
51+
force_start_nonvoter_as_single_member,
5152
initial_members_query
5253
].
5354

@@ -784,30 +785,80 @@ force_start_follower_as_single_member_nonvoter(Config) ->
784785
ServerId4 = ?config(server_id4, Config),
785786
UId4 = ?config(uid4, Config),
786787
Conf4 = conf(ClusterName, UId4, ServerId4, PrivDir, [ServerId3]),
787-
{ok, _, _} = ra:add_member(ServerId3, #{id => ServerId4, membership => promotable, uid => <<"test4">>}),
788+
{ok, _, _} = ra:add_member(ServerId3, #{id => ServerId4, membership => promotable, uid => UId4}),
788789
%% the membership has changed but member not running yet
789790
%% it is nonvoter and does not affect quorum size
790791
{ok, _, _} = ra:process_command(ServerId3, {enq, banana}),
791792
%% start new member
792-
ok = ra:start_server(?SYS, Conf4#{membership => promotable, uid => <<"test4">>}),
793+
ok = ra:start_server(?SYS, Conf4#{membership => promotable, uid => UId4}),
793794
{ok, _, ServerId3} = ra:members(ServerId4),
794795
ok = enqueue(ServerId3, msg3),
795796

796797
%% add a non-voter member
797798
ServerId5 = ?config(server_id5, Config),
798799
UId5 = ?config(uid5, Config),
799800
Conf5 = conf(ClusterName, UId5, ServerId5, PrivDir, [ServerId3]),
800-
{ok, _, _} = ra:add_member(ServerId3, #{id => ServerId5, membership => non_voter, uid => <<"test5">>}),
801+
{ok, _, _} = ra:add_member(ServerId3, #{id => ServerId5, membership => non_voter, uid => UId5}),
801802
%% the membership has changed but member not running yet
802803
%% it is nonvoter and does not affect quorum size
803804
{ok, _, _} = ra:process_command(ServerId3, {enq, banana}),
804805
%% start new member
805-
ok = ra:start_server(?SYS, Conf5#{membership => non_voter, uid => <<"test5">>}),
806+
ok = ra:start_server(?SYS, Conf5#{membership => non_voter, uid => UId5}),
806807
{ok, _, ServerId3} = ra:members(ServerId5),
807808
ok = enqueue(ServerId3, msg4),
808809

809810
ok.
810811

812+
force_start_nonvoter_as_single_member(Config) ->
813+
ok = logger:set_primary_config(level, all),
814+
ClusterName = ?config(cluster_name, Config),
815+
PrivDir = ?config(priv_dir, Config),
816+
ServerId1 = ?config(server_id, Config),
817+
ServerId2 = ?config(server_id2, Config),
818+
ServerId3 = ?config(server_id3, Config),
819+
InitialCluster = [ServerId1, ServerId2, ServerId3],
820+
ok = start_cluster(ClusterName, InitialCluster),
821+
timer:sleep(100),
822+
823+
ServerId4 = ?config(server_id4, Config),
824+
UId4 = ?config(uid4, Config),
825+
Conf4 = conf(ClusterName, UId4, ServerId4, PrivDir, InitialCluster),
826+
{ok, _, _} = ra:add_member(ServerId3, #{id => ServerId4, membership => non_voter, uid => UId4}),
827+
ok = ra:start_server(?SYS, Conf4#{membership => non_voter, uid => UId4}),
828+
829+
%% stop voters to simulate permanent outage
830+
ok = ra:stop_server(?SYS, ServerId1),
831+
ok = ra:stop_server(?SYS, ServerId2),
832+
ok = ra:stop_server(?SYS, ServerId3),
833+
timer:sleep(100),
834+
835+
%% force the remaining node to change it's membership
836+
ok = ra_server_proc:force_shrink_members_to_current_member(ServerId4),
837+
{ok, [_], ServerId4} = ra:members(ServerId4),
838+
ok = enqueue(ServerId4, msg1),
839+
840+
%% test that it works after restart
841+
ok = ra:stop_server(?SYS, ServerId4),
842+
ok = ra:restart_server(?SYS, ServerId4),
843+
{ok, [_], ServerId4} = ra:members(ServerId4),
844+
ok = enqueue(ServerId4, msg2),
845+
846+
847+
%% add a non-voter member
848+
ServerId5 = ?config(server_id5, Config),
849+
UId5 = ?config(uid5, Config),
850+
Conf5 = conf(ClusterName, UId5, ServerId5, PrivDir, [ServerId4]),
851+
{ok, _, _} = ra:add_member(ServerId4, #{id => ServerId5, membership => non_voter, uid => UId5}),
852+
%% the membership has changed but member not running yet
853+
%% it is nonvoter and does not affect quorum size
854+
ok = enqueue(ServerId4, msg3),
855+
%% start new member
856+
ok = ra:start_server(?SYS, Conf5#{membership => non_voter, uid => UId5}),
857+
{ok, _, ServerId4} = ra:members(ServerId5),
858+
ok = enqueue(ServerId4, msg4),
859+
860+
ok.
861+
811862
initial_members_query(Config) ->
812863
ok = logger:set_primary_config(level, all),
813864
%% ra:start_server should fail if the node already exists

0 commit comments

Comments
 (0)