@@ -480,10 +480,12 @@ impl<C: RaftTypeConfig, N: RaftNetworkFactory<C>, S: RaftStorage<C>> RaftCore<C,
480
480
///
481
481
/// Adding a learner does not affect election, thus it does not need to enter joint consensus.
482
482
///
483
+ /// TODO: It has to wait for the previous membership to commit.
484
+ /// TODO: Otherwise a second proposed membership implies the previous one is committed.
485
+ /// TODO: Test it.
486
+ /// TODO: This limit can be removed if membership_state is replaced by a list of membership logs.
487
+ /// TODO: Because allowing this requires the engine to be able to store more than 2 membership logs.
483
488
/// And it does not need to wait for the previous membership log to commit to propose the new membership log.
484
- ///
485
- /// If `blocking` is `true`, the result is sent to `tx` as the target node log has caught up. Otherwise, result is
486
- /// sent at once, no matter whether the target node log is lagging or not.
487
489
#[ tracing:: instrument( level = "debug" , skip_all) ]
488
490
pub ( super ) async fn add_learner (
489
491
& mut self ,
@@ -553,13 +555,6 @@ impl<C: RaftTypeConfig, N: RaftNetworkFactory<C>, S: RaftStorage<C>> RaftCore<C,
553
555
Ok ( ( ) )
554
556
}
555
557
556
- /// return true if there is pending uncommitted config change
557
- fn has_pending_config ( & self ) -> bool {
558
- // The last membership config is not committed yet.
559
- // Can not process the next one.
560
- self . engine . state . committed < self . engine . state . membership_state . effective . log_id
561
- }
562
-
563
558
/// Submit change-membership by writing a Membership log entry, if the `expect` is satisfied.
564
559
///
565
560
/// If `turn_to_learner` is `true`, removed `voter` will becomes `learner`. Otherwise they will be just removed.
@@ -582,13 +577,9 @@ impl<C: RaftTypeConfig, N: RaftNetworkFactory<C>, S: RaftStorage<C>> RaftCore<C,
582
577
return Ok ( ( ) ) ;
583
578
}
584
579
585
- if self . has_pending_config ( ) {
586
- let _ = tx. send ( Err ( ClientWriteError :: ChangeMembershipError (
587
- ChangeMembershipError :: InProgress ( InProgress {
588
- // has_pending_config() implies an existing membership log.
589
- membership_log_id : self . engine . state . membership_state . effective . log_id . unwrap ( ) ,
590
- } ) ,
591
- ) ) ) ;
580
+ let res = self . check_membership_committed ( ) ;
581
+ if let Err ( e) = res {
582
+ let _ = tx. send ( Err ( ClientWriteError :: ChangeMembershipError ( e) ) ) ;
592
583
return Ok ( ( ) ) ;
593
584
}
594
585
@@ -631,6 +622,20 @@ impl<C: RaftTypeConfig, N: RaftNetworkFactory<C>, S: RaftStorage<C>> RaftCore<C,
631
622
Ok ( ( ) )
632
623
}
633
624
625
+ /// Check if the effective membership is committed, so that a new membership is allowed to be proposed.
626
+ fn check_membership_committed ( & self ) -> Result < ( ) , ChangeMembershipError < C :: NodeId > > {
627
+ let st = & self . engine . state ;
628
+
629
+ if st. is_membership_committed ( ) {
630
+ return Ok ( ( ) ) ;
631
+ }
632
+
633
+ Err ( ChangeMembershipError :: InProgress ( InProgress {
634
+ committed : st. committed ,
635
+ membership_log_id : st. membership_state . effective . log_id ,
636
+ } ) )
637
+ }
638
+
634
639
/// return Ok if all the current replication states satisfy the `expectation` for changing membership.
635
640
fn check_replication_states < ' n > (
636
641
& self ,
@@ -1005,11 +1010,11 @@ impl<C: RaftTypeConfig, N: RaftNetworkFactory<C>, S: RaftStorage<C>> RaftCore<C,
1005
1010
1006
1011
tracing:: debug!( last_applied = display( last_applied) , "update last_applied" ) ;
1007
1012
1008
- if let Some ( leader_data ) = & mut self . leader_data {
1013
+ if let Some ( l ) = & mut self . leader_data {
1009
1014
let mut results = apply_results. into_iter ( ) ;
1010
1015
1011
1016
for log_index in since..end {
1012
- let tx = leader_data . client_resp_channels . remove ( & log_index) ;
1017
+ let tx = l . client_resp_channels . remove ( & log_index) ;
1013
1018
1014
1019
let i = log_index - since;
1015
1020
let entry = & entries[ i as usize ] ;
@@ -1023,6 +1028,7 @@ impl<C: RaftTypeConfig, N: RaftNetworkFactory<C>, S: RaftStorage<C>> RaftCore<C,
1023
1028
Ok ( ( ) )
1024
1029
}
1025
1030
1031
+ /// Send result of applying a log entry to its client.
1026
1032
#[ tracing:: instrument( level = "debug" , skip_all) ]
1027
1033
pub ( super ) async fn send_response (
1028
1034
entry : & Entry < C > ,
@@ -1460,7 +1466,10 @@ impl<C: RaftTypeConfig, N: RaftNetworkFactory<C>, S: RaftStorage<C>> RaftCore<C,
1460
1466
1461
1467
match msg {
1462
1468
RaftMsg :: AppendEntries { rpc, tx } => {
1463
- let _ = tx. send ( self . handle_append_entries_request ( rpc) . await . extract_fatal ( ) ?) ;
1469
+ let resp =
1470
+ self . engine . handle_append_entries_req ( & rpc. vote , rpc. prev_log_id , & rpc. entries , rpc. leader_commit ) ;
1471
+ self . run_engine_commands ( rpc. entries . as_slice ( ) ) . await ?;
1472
+ let _ = tx. send ( Ok ( resp) ) ;
1464
1473
}
1465
1474
RaftMsg :: RequestVote { rpc, tx } => {
1466
1475
let _ = tx. send ( self . handle_vote_request ( rpc) . await . extract_fatal ( ) ?) ;
@@ -1773,8 +1782,8 @@ impl<C: RaftTypeConfig, N: RaftNetworkFactory<C>, S: RaftStorage<C>> RaftRuntime
1773
1782
self . leader_commit ( i) . await ?;
1774
1783
}
1775
1784
}
1776
- Command :: FollowerCommit { upto : _ , .. } => {
1777
- self . replicate_to_state_machine_if_needed ( ) . await ?;
1785
+ Command :: FollowerCommit { upto, .. } => {
1786
+ self . apply_to_state_machine ( upto . index ) . await ?;
1778
1787
}
1779
1788
Command :: ReplicateInputEntries { range } => {
1780
1789
if let Some ( last) = range. clone ( ) . last ( ) {
0 commit comments