From 16d4f6f253e1b3af81ddd808d24a38d5710a3b57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=82=8E=E6=B3=BC?= Date: Sun, 24 Mar 2024 19:34:17 +0800 Subject: [PATCH] Refactor: add `noop_log_id` to `Leading` When a leader established, store the first log id in struct `Leading`, i.e., the noop log id. This log id can be used for linearizable read. --- openraft/src/engine/engine_impl.rs | 12 ++++++++++++ openraft/src/engine/handler/vote_handler/mod.rs | 4 ++-- openraft/src/engine/tests/elect_test.rs | 11 +++++++++++ openraft/src/engine/tests/handle_vote_resp_test.rs | 7 +++++++ openraft/src/leader/leader.rs | 7 +++++++ 5 files changed, 39 insertions(+), 2 deletions(-) diff --git a/openraft/src/engine/engine_impl.rs b/openraft/src/engine/engine_impl.rs index 2ca3a3fe5..09c8f31d6 100644 --- a/openraft/src/engine/engine_impl.rs +++ b/openraft/src/engine/engine_impl.rs @@ -645,6 +645,18 @@ where C: RaftTypeConfig debug_assert!(_res.is_ok(), "commit vote can not fail but: {:?}", _res); } + // Update the noop log index + { + let vote = *self.state.vote_ref(); + let index = self.state.last_log_id().next_index(); + + let leading = self.internal_server_state.leading_mut().unwrap(); + + // TODO: in future the leader will be able to start another new election without quit leader. + debug_assert!(leading.noop_log_id.is_none()); + leading.noop_log_id = Some(LogId::new(vote.committed_leader_id().unwrap(), index)); + } + let mut rh = self.replication_handler(); // It has to setup replication stream first because append_blank_log() may update the diff --git a/openraft/src/engine/handler/vote_handler/mod.rs b/openraft/src/engine/handler/vote_handler/mod.rs index 08a514a08..0190d13f6 100644 --- a/openraft/src/engine/handler/vote_handler/mod.rs +++ b/openraft/src/engine/handler/vote_handler/mod.rs @@ -147,7 +147,7 @@ where C: RaftTypeConfig // Re-create a new Leader instance. let em = &self.state.membership_state.effective(); - let leader = Leading::new( + let leading = Leading::new( *self.state.vote_ref(), em.membership().to_quorum_set(), em.learner_ids(), @@ -156,7 +156,7 @@ where C: RaftTypeConfig // Do not update clock_progress, until the first blank log is committed. - *self.internal_server_state = InternalServerState::Leading(Box::new(leader)); + *self.internal_server_state = InternalServerState::Leading(Box::new(leading)); self.server_state_handler().update_server_state_if_changed(); } diff --git a/openraft/src/engine/tests/elect_test.rs b/openraft/src/engine/tests/elect_test.rs index ef6c399b7..30dbbace6 100644 --- a/openraft/src/engine/tests/elect_test.rs +++ b/openraft/src/engine/tests/elect_test.rs @@ -49,6 +49,10 @@ fn test_elect() -> anyhow::Result<()> { eng.elect(); assert_eq!(Vote::new_committed(1, 1), *eng.state.vote_ref()); + assert_eq!( + Some(log_id(1, 1, 1)), + eng.internal_server_state.leading().unwrap().noop_log_id + ); assert!( eng.internal_server_state.voting_mut().is_none(), "voting state is removed when becoming leader" @@ -102,6 +106,11 @@ fn test_elect() -> anyhow::Result<()> { eng.elect(); assert_eq!(Vote::new_committed(2, 1), *eng.state.vote_ref()); + assert_eq!( + Some(log_id(2, 1, 1)), + eng.internal_server_state.leading().unwrap().noop_log_id + ); + assert!( eng.internal_server_state.voting_mut().is_none(), "voting state is removed when becoming leader" @@ -151,6 +160,8 @@ fn test_elect() -> anyhow::Result<()> { eng.elect(); assert_eq!(Vote::new(1, 1), *eng.state.vote_ref()); + assert_eq!(None, eng.internal_server_state.leading().unwrap().noop_log_id); + assert_eq!( Some(btreeset! {1},), eng.internal_server_state.leading().map(|x| x.voting().unwrap().granters().collect::>()) diff --git a/openraft/src/engine/tests/handle_vote_resp_test.rs b/openraft/src/engine/tests/handle_vote_resp_test.rs index b3c43520c..6e26f9f6c 100644 --- a/openraft/src/engine/tests/handle_vote_resp_test.rs +++ b/openraft/src/engine/tests/handle_vote_resp_test.rs @@ -90,6 +90,7 @@ fn test_handle_vote_resp() -> anyhow::Result<()> { }); assert_eq!(Vote::new(2, 1), *eng.state.vote_ref()); + assert_eq!(None, eng.internal_server_state.leading().unwrap().noop_log_id); assert_eq!( Some(btreeset! {1},), eng.internal_server_state.leading().map(|x| x.voting().unwrap().granters().collect::>()) @@ -163,6 +164,7 @@ fn test_handle_vote_resp() -> anyhow::Result<()> { }); assert_eq!(Vote::new(2, 1), *eng.state.vote_ref()); + assert_eq!(None, eng.internal_server_state.leading().unwrap().noop_log_id); assert_eq!( Some(btreeset! {1},), eng.internal_server_state.leading().map(|x| x.voting().unwrap().granters().collect::>()) @@ -199,6 +201,7 @@ fn test_handle_vote_resp() -> anyhow::Result<()> { }); assert_eq!(Vote::new(2, 1), *eng.state.vote_ref()); + assert_eq!(None, eng.internal_server_state.leading().unwrap().noop_log_id); assert_eq!( Some(btreeset! {1,2},), eng.internal_server_state.leading().map(|x| x.voting().unwrap().granters().collect::>()) @@ -235,6 +238,10 @@ fn test_handle_vote_resp() -> anyhow::Result<()> { }); assert_eq!(Vote::new_committed(2, 1), *eng.state.vote_ref()); + assert_eq!( + Some(log_id(2, 1, 1)), + eng.internal_server_state.leading().unwrap().noop_log_id + ); assert!( eng.internal_server_state.voting_mut().is_none(), "voting state is removed when becoming leader" diff --git a/openraft/src/leader/leader.rs b/openraft/src/leader/leader.rs index c31840d53..66275b21b 100644 --- a/openraft/src/leader/leader.rs +++ b/openraft/src/leader/leader.rs @@ -34,6 +34,12 @@ where C: RaftTypeConfig /// The vote this leader works in. pub(crate) vote: Vote, + /// The log id of the first log entry proposed by this leader, + /// i.e., the `noop` log(AKA blank log) after leader established. + /// + /// It is set when leader established. + pub(crate) noop_log_id: Option>, + quorum_set: QS, /// Voting state, i.e., there is a Candidate running. @@ -65,6 +71,7 @@ where Self { vote, + noop_log_id: None, quorum_set: quorum_set.clone(), voting: None, progress: VecProgress::new(