@@ -1506,6 +1506,50 @@ witness({call, From}, ?SNAPSHOT_AVAILABLE_COMMAND(_, #raft_log_pos{index = Snaps
1506
1506
{keep_state_and_data , {reply , From , {error , rejected }}}
1507
1507
end ;
1508
1508
1509
+ % % [RequestVote] A witness with an unallocated vote should decide if the requesting candidate is eligible to receive
1510
+ % % its vote for the current term and affirm or reject accordingly.
1511
+ witness (_Type , ? REMOTE (? IDENTITY_REQUIRES_MIGRATION (_ , CandidateId ) = Candidate , ? REQUEST_VOTE (_ElectionType , CandidateIndex , CandidateTerm )),
1512
+ # raft_state {name = Name , log_view = View , current_term = CurrentTerm , voted_for = undefined } = State ) ->
1513
+ Index = wa_raft_log :last_index (View ),
1514
+ {ok , Term } = wa_raft_log :term (View , Index ),
1515
+ % Witnesses should only vote for candidates whose logs are at least as up-to-date as the local log.
1516
+ % Logs are ordered in up-to-dateness by the lexicographic order of the {Term, Index} pair of their latest entry. (5.4.1)
1517
+ case {CandidateTerm , CandidateIndex } >= {Term , Index } of
1518
+ true ->
1519
+ ? LOG_NOTICE (" Server[~0p , term ~0p , witness] decides to vote for candidate ~0p with up-to-date log at ~0p :~0p versus local log at ~0p :~0p ." ,
1520
+ [Name , CurrentTerm , Candidate , CandidateIndex , CandidateTerm , Index , Term ], #{domain => [whatsapp , wa_raft ]}),
1521
+ NewState = State # raft_state {voted_for = CandidateId },
1522
+ % Persist the vote to stable storage before responding to the vote request. (Fig. 2)
1523
+ wa_raft_durable_state :store (NewState ),
1524
+ send_rpc (Candidate , ? VOTE (true ), State ),
1525
+ {keep_state , NewState };
1526
+ false ->
1527
+ ? LOG_NOTICE (" Server[~0p , term ~0p , witness] refuses to vote for candidate ~0p with outdated log at ~0p :~0p versus local log at ~0p :~0p ." ,
1528
+ [Name , CurrentTerm , Candidate , CandidateIndex , CandidateTerm , Index , Term ], #{domain => [whatsapp , wa_raft ]}),
1529
+ send_rpc (Candidate , ? VOTE (false ), State ),
1530
+ keep_state_and_data
1531
+ end ;
1532
+ % % [RequestVote] A witness should affirm any vote requests for the candidate it already voted for in the current term.
1533
+ witness (_Type , ? REMOTE (? IDENTITY_REQUIRES_MIGRATION (_ , CandidateId ) = Candidate , ? REQUEST_VOTE (_ElectionType , _CandidateIndex , _CandidateTerm )),
1534
+ # raft_state {name = Name , current_term = CurrentTerm , voted_for = CandidateId } = State ) ->
1535
+ ? LOG_NOTICE (" Server[~0p , term ~0p , witness] repeating prior vote for candidate ~0p ." ,
1536
+ [Name , CurrentTerm , Candidate ], #{domain => [whatsapp , wa_raft ]}),
1537
+ send_rpc (Candidate , ? VOTE (true ), State ),
1538
+ keep_state_and_data ;
1539
+ % % [RequestVote] A witness should reject any vote requests for the candidate it did not vote for in the current term.
1540
+ witness (_Type , ? REMOTE (Candidate , ? REQUEST_VOTE (_ElectionType , _CandidateIndex , _CandidateTerm )),
1541
+ # raft_state {name = Name , current_term = CurrentTerm , voted_for = VotedFor } = State ) ->
1542
+ ? LOG_NOTICE (" Server[~0p , term ~0p , witness] refusing to vote for candidate ~0p after previously voting for candidate ~0p in the current term." ,
1543
+ [Name , CurrentTerm , Candidate , VotedFor ], #{domain => [whatsapp , wa_raft ]}),
1544
+ send_rpc (Candidate , ? VOTE (false ), State ),
1545
+ keep_state_and_data ;
1546
+
1547
+ % % [Vote] A witness should ignore any votes because its not eligible for leadership
1548
+ witness (_Type , ? REMOTE (Sender , ? VOTE (Voted )), # raft_state {name = Name , current_term = CurrentTerm }) ->
1549
+ ? LOG_WARNING (" Server[~0p , term ~0p , witness] got unexecpted vote ~p from ~p ." ,
1550
+ [Name , CurrentTerm , Voted , Sender ], #{domain => [whatsapp , wa_raft ]}),
1551
+ keep_state_and_data ;
1552
+
1509
1553
% % [Witness] Witness receives RAFT command
1510
1554
witness (Type , ? RAFT_COMMAND (_COMMAND , _Payload ) = Event , State ) ->
1511
1555
command (? FUNCTION_NAME , Type , Event , State );
0 commit comments