From c154038574cac5bf1acd44e1da041c96647b65dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=82=8E=E6=B3=BC?= Date: Thu, 2 Jan 2025 16:56:34 +0800 Subject: [PATCH 1/4] Feature: Abstract `Vote` - Added an associated type `Vote: RaftVote` to `RaftTypeConfig`, allowing applications to customize the `Vote` implementation. - Introduced the `OrdBy` trait with the method `fn ord_by() -> Option` to enable customized ordering for types. This trait is used internally only for determine `RaftVote` order. The ordering logic is consistent across different `Vote` implementations and does not require the application to implement `PartialOrd` directly for `Vote`. Instead, this ordering property is provided by OpenRaft. - Implemented `RaftVote` for the struct `Vote`, which serves as the default `RaftVote` implementation. This ensures that applications upgrading OpenRaft do not need to make any changes. - Part of #1278 --- examples/raft-kv-memstore-grpc/build.rs | 1 + examples/raft-kv-memstore-grpc/src/lib.rs | 31 +---- .../src/pb_impl/impl_vote.rs | 34 +++++ .../raft-kv-memstore-grpc/src/pb_impl/mod.rs | 1 + examples/utils/declare_types.rs | 2 +- openraft/src/base/mod.rs | 2 + openraft/src/base/ord_by.rs | 36 +++++ openraft/src/core/raft_core.rs | 8 +- openraft/src/core/raft_msg/mod.rs | 2 +- openraft/src/core/tick.rs | 3 +- openraft/src/engine/engine_impl.rs | 17 +-- .../engine/handler/establish_handler/mod.rs | 7 +- .../following_handler/append_entries_test.rs | 12 +- .../following_handler/commit_entries_test.rs | 1 + .../do_append_entries_test.rs | 1 + .../install_snapshot_test.rs | 12 +- .../leader_handler/append_entries_test.rs | 1 + .../leader_handler/send_heartbeat_test.rs | 1 + .../engine/handler/replication_handler/mod.rs | 1 + .../vote_handler/become_leader_test.rs | 1 + .../src/engine/handler/vote_handler/mod.rs | 11 +- openraft/src/engine/testing.rs | 3 +- .../src/engine/tests/append_entries_test.rs | 1 + .../src/engine/tests/handle_vote_resp_test.rs | 1 + openraft/src/engine/tests/initialize_test.rs | 1 + .../tests/install_full_snapshot_test.rs | 3 +- openraft/src/engine/tests/startup_test.rs | 1 + openraft/src/impls/mod.rs | 8 ++ openraft/src/metrics/metric.rs | 3 +- openraft/src/metrics/raft_metrics.rs | 3 +- openraft/src/metrics/wait_test.rs | 2 +- openraft/src/network/snapshot_transport.rs | 3 +- openraft/src/proposer/candidate.rs | 2 + openraft/src/proposer/leader.rs | 5 +- openraft/src/raft/declare_raft_types_test.rs | 1 + openraft/src/raft/mod.rs | 8 +- openraft/src/raft_state/io_state/io_id.rs | 6 +- openraft/src/raft_state/mod.rs | 14 +- openraft/src/replication/mod.rs | 9 +- .../src/storage/v2/raft_log_storage_ext.rs | 1 + openraft/src/testing/log/suite.rs | 18 +-- openraft/src/type_config.rs | 9 +- openraft/src/vote/committed.rs | 42 ++++-- openraft/src/vote/mod.rs | 5 +- openraft/src/vote/non_committed.rs | 35 +++-- openraft/src/vote/raft_vote.rs | 123 ++++++++++++++++++ openraft/src/vote/ref_vote.rs | 12 +- openraft/src/vote/vote.rs | 65 +++------ tests/tests/fixtures/mod.rs | 77 ++++++++++- 49 files changed, 472 insertions(+), 174 deletions(-) create mode 100644 examples/raft-kv-memstore-grpc/src/pb_impl/impl_vote.rs create mode 100644 openraft/src/base/ord_by.rs create mode 100644 openraft/src/vote/raft_vote.rs diff --git a/examples/raft-kv-memstore-grpc/build.rs b/examples/raft-kv-memstore-grpc/build.rs index dcaa873a4..36a5cdbc1 100644 --- a/examples/raft-kv-memstore-grpc/build.rs +++ b/examples/raft-kv-memstore-grpc/build.rs @@ -24,6 +24,7 @@ fn main() -> Result<(), Box> { "openraftpb.LeaderId", "#[derive(Eq, serde::Serialize, serde::Deserialize)]", ) + .type_attribute("openraftpb.Vote", "#[derive(Eq, serde::Serialize, serde::Deserialize)]") .compile_protos_with_config(config, &proto_files, &["proto"])?; Ok(()) } diff --git a/examples/raft-kv-memstore-grpc/src/lib.rs b/examples/raft-kv-memstore-grpc/src/lib.rs index 0008f1a4f..3a5bd481c 100644 --- a/examples/raft-kv-memstore-grpc/src/lib.rs +++ b/examples/raft-kv-memstore-grpc/src/lib.rs @@ -20,6 +20,7 @@ openraft::declare_raft_types!( D = pb::SetRequest, R = pb::Response, LeaderId = pb::LeaderId, + Vote = pb::Vote, Node = pb::Node, SnapshotData = StateMachineData, ); @@ -34,17 +35,6 @@ pub mod protobuf { #[path = "../../utils/declare_types.rs"] pub mod typ; -impl From for Vote { - fn from(proto_vote: pb::Vote) -> Self { - let leader_id: LeaderId = proto_vote.leader_id.unwrap(); - if proto_vote.committed { - Vote::new_committed(leader_id.term, leader_id.node_id) - } else { - Vote::new(leader_id.term, leader_id.node_id) - } - } -} - impl From for LogId { fn from(proto_log_id: pb::LogId) -> Self { LogId::new(proto_log_id.term, proto_log_id.index) @@ -53,7 +43,7 @@ impl From for LogId { impl From for VoteRequest { fn from(proto_vote_req: pb::VoteRequest) -> Self { - let vote: Vote = proto_vote_req.vote.unwrap().into(); + let vote = proto_vote_req.vote.unwrap(); let last_log_id = proto_vote_req.last_log_id.map(|log_id| log_id.into()); VoteRequest::new(vote, last_log_id) } @@ -61,23 +51,12 @@ impl From for VoteRequest { impl From for VoteResponse { fn from(proto_vote_resp: pb::VoteResponse) -> Self { - let vote: Vote = proto_vote_resp.vote.unwrap().into(); + let vote = proto_vote_resp.vote.unwrap(); let last_log_id = proto_vote_resp.last_log_id.map(|log_id| log_id.into()); VoteResponse::new(vote, last_log_id, proto_vote_resp.vote_granted) } } -impl From for pb::Vote { - fn from(vote: Vote) -> Self { - pb::Vote { - leader_id: Some(pb::LeaderId { - term: vote.leader_id().term, - node_id: vote.leader_id().node_id, - }), - committed: vote.is_committed(), - } - } -} impl From for pb::LogId { fn from(log_id: LogId) -> Self { pb::LogId { @@ -90,7 +69,7 @@ impl From for pb::LogId { impl From for pb::VoteRequest { fn from(vote_req: VoteRequest) -> Self { pb::VoteRequest { - vote: Some(vote_req.vote.into()), + vote: Some(vote_req.vote), last_log_id: vote_req.last_log_id.map(|log_id| log_id.into()), } } @@ -99,7 +78,7 @@ impl From for pb::VoteRequest { impl From for pb::VoteResponse { fn from(vote_resp: VoteResponse) -> Self { pb::VoteResponse { - vote: Some(vote_resp.vote.into()), + vote: Some(vote_resp.vote), vote_granted: vote_resp.vote_granted, last_log_id: vote_resp.last_log_id.map(|log_id| log_id.into()), } diff --git a/examples/raft-kv-memstore-grpc/src/pb_impl/impl_vote.rs b/examples/raft-kv-memstore-grpc/src/pb_impl/impl_vote.rs new file mode 100644 index 000000000..5d0a99c44 --- /dev/null +++ b/examples/raft-kv-memstore-grpc/src/pb_impl/impl_vote.rs @@ -0,0 +1,34 @@ +use std::fmt; + +use openraft::vote::RaftVote; + +use crate::typ::*; +use crate::TypeConfig; + +impl RaftVote for Vote { + fn from_leader_id(leader_id: LeaderId, committed: bool) -> Self { + Vote { + leader_id: Some(leader_id), + committed, + } + } + + fn leader_id(&self) -> Option<&LeaderId> { + self.leader_id.as_ref() + } + + fn is_committed(&self) -> bool { + self.committed + } +} + +impl fmt::Display for Vote { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "<{}:{}>", + self.leader_id.as_ref().unwrap(), + if self.is_committed() { "Q" } else { "-" } + ) + } +} diff --git a/examples/raft-kv-memstore-grpc/src/pb_impl/mod.rs b/examples/raft-kv-memstore-grpc/src/pb_impl/mod.rs index df953a355..215d5a347 100644 --- a/examples/raft-kv-memstore-grpc/src/pb_impl/mod.rs +++ b/examples/raft-kv-memstore-grpc/src/pb_impl/mod.rs @@ -1,3 +1,4 @@ //! Implements traits for protobuf types mod impl_leader_id; +mod impl_vote; diff --git a/examples/utils/declare_types.rs b/examples/utils/declare_types.rs index 12e91b1c5..74d8b800b 100644 --- a/examples/utils/declare_types.rs +++ b/examples/utils/declare_types.rs @@ -3,7 +3,7 @@ use super::TypeConfig; pub type Raft = openraft::Raft; -pub type Vote = openraft::Vote; +pub type Vote = ::Vote; pub type LeaderId = ::LeaderId; pub type LogId = openraft::LogId; pub type Entry = openraft::Entry; diff --git a/openraft/src/base/mod.rs b/openraft/src/base/mod.rs index 43d621ab8..6ced31a13 100644 --- a/openraft/src/base/mod.rs +++ b/openraft/src/base/mod.rs @@ -1,5 +1,7 @@ //! Basic types used in the Raft implementation. +pub(crate) mod ord_by; + pub use serde_able::OptionalSerde; pub use threaded::BoxAny; pub use threaded::BoxAsyncOnceMut; diff --git a/openraft/src/base/ord_by.rs b/openraft/src/base/ord_by.rs new file mode 100644 index 000000000..a7b35e91f --- /dev/null +++ b/openraft/src/base/ord_by.rs @@ -0,0 +1,36 @@ +/// A trait for types whose order can be determined by a key. +/// +/// Types implementing this trait define how they should be compared by providing a key +/// that implements [`PartialOrd`]. +/// +/// OpenRaft uses this trait to compare types that may not be [`PartialOrd`] themselves. +/// +/// # Type Parameters +/// - `Key<'k>`: The type of the comparison key, which must be partially ordered and must not out +/// live the value. +/// +/// # Examples +/// ```rust,ignore +/// # use openraft::base::ord_by::OrdBy; +/// +/// struct Person { +/// name: String, +/// age: u32, +/// } +/// +/// impl OrdBy<()> for Person { +/// type By<'k> = &'k str; +/// +/// fn ord_by(&self) -> Self::By<'_> { +/// &self.name +/// } +/// } +/// ``` +pub(crate) trait OrdBy { + /// The key type used for comparison. + type By<'k>: PartialOrd + 'k + where Self: 'k; + + /// Returns the key used for comparing this value. + fn ord_by(&self) -> Self::By<'_>; +} diff --git a/openraft/src/core/raft_core.rs b/openraft/src/core/raft_core.rs index 72d93ad4d..230815e85 100644 --- a/openraft/src/core/raft_core.rs +++ b/openraft/src/core/raft_core.rs @@ -95,8 +95,10 @@ use crate::type_config::async_runtime::MpscUnboundedReceiver; use crate::type_config::TypeConfigExt; use crate::vote::committed::CommittedVote; use crate::vote::non_committed::NonCommittedVote; +use crate::vote::raft_vote::RaftVoteExt; use crate::vote::vote_status::VoteStatus; use crate::vote::RaftLeaderId; +use crate::vote::RaftVote; use crate::ChangeMembers; use crate::Instant; use crate::LogId; @@ -392,7 +394,7 @@ where // request. if let AppendEntriesResponse::HigherVote(vote) = append_res { debug_assert!( - vote > my_vote, + vote.as_ref_vote() > my_vote.as_ref_vote(), "committed vote({}) has total order relation with other votes({})", my_vote, vote @@ -584,7 +586,7 @@ where id: self.id.clone(), // --- data --- - current_term: st.vote_ref().leader_id().term(), + current_term: st.vote_ref().to_leader_id().term(), vote: st.io_state().io_progress.flushed().map(|io_id| io_id.to_vote()).unwrap_or_default(), last_log_index: st.last_log_id().index(), last_applied: st.io_applied().cloned(), @@ -727,7 +729,7 @@ where } // Safe unwrap(): vote that is committed has to already have voted for some node. - let id = vote.leader_id().node_id().cloned().unwrap(); + let id = vote.to_leader_id().node_id().cloned().unwrap(); // TODO: `is_voter()` is slow, maybe cache `current_leader`, // e.g., only update it when membership or vote changes diff --git a/openraft/src/core/raft_msg/mod.rs b/openraft/src/core/raft_msg/mod.rs index 70df7399d..c7212d402 100644 --- a/openraft/src/core/raft_msg/mod.rs +++ b/openraft/src/core/raft_msg/mod.rs @@ -61,7 +61,7 @@ where C: RaftTypeConfig /// /// Returns a snapshot data handle for receiving data. /// - /// It does not check [`Vote`] because it is a read operation + /// It does not check `Vote` because it is a read operation /// and does not break raft protocol. BeginReceivingSnapshot { tx: ResultSender>, Infallible>, diff --git a/openraft/src/core/tick.rs b/openraft/src/core/tick.rs index 425d0b7d2..1b098dd6e 100644 --- a/openraft/src/core/tick.rs +++ b/openraft/src/core/tick.rs @@ -181,7 +181,8 @@ mod tests { type Node = (); type Term = u64; type LeaderId = crate::impls::leader_id_adv::LeaderId; - type Entry = crate::Entry; + type Vote = crate::impls::Vote; + type Entry = crate::Entry; type SnapshotData = Cursor>; type AsyncRuntime = TokioRuntime; type Responder = crate::impls::OneshotResponder; diff --git a/openraft/src/engine/engine_impl.rs b/openraft/src/engine/engine_impl.rs index 071e5de5a..a6b51bcd4 100644 --- a/openraft/src/engine/engine_impl.rs +++ b/openraft/src/engine/engine_impl.rs @@ -2,6 +2,7 @@ use std::time::Duration; use validit::Valid; +use crate::base::ord_by::OrdBy; use crate::core::raft_msg::AppendEntriesTx; use crate::core::raft_msg::ResultSender; use crate::core::sm; @@ -44,18 +45,20 @@ use crate::raft_state::LogStateReader; use crate::raft_state::RaftState; use crate::storage::Snapshot; use crate::storage::SnapshotMeta; +use crate::type_config::alias::LeaderIdOf; use crate::type_config::alias::ResponderOf; use crate::type_config::alias::SnapshotDataOf; use crate::type_config::alias::VoteOf; use crate::type_config::TypeConfigExt; +use crate::vote::raft_vote::RaftVoteExt; use crate::vote::RaftLeaderId; use crate::vote::RaftTerm; +use crate::vote::RaftVote; use crate::LogId; use crate::LogIdOptionExt; use crate::Membership; use crate::RaftLogId; use crate::RaftTypeConfig; -use crate::Vote; /// Raft protocol algorithm. /// @@ -195,10 +198,7 @@ where C: RaftTypeConfig self.check_members_contain_me(m)?; // FollowingHandler requires vote to be committed. - let vote = Vote { - committed: true, - ..Default::default() - }; + let vote = as RaftVote>::from_leader_id(Default::default(), true); self.state.vote.update(C::now(), Duration::default(), vote); self.following_handler().do_append_entries(vec![entry]); @@ -211,8 +211,9 @@ where C: RaftTypeConfig /// Start to elect this node as leader #[tracing::instrument(level = "debug", skip(self))] pub(crate) fn elect(&mut self) { - let new_term = self.state.vote.leader_id().term().next(); - let new_vote = Vote::new(new_term, self.config.id.clone()); + let new_term = self.state.vote.term().next(); + let leader_id = LeaderIdOf::::new(new_term, self.config.id.clone()); + let new_vote = VoteOf::::from_leader_id(leader_id, false); let candidate = self.new_candidate(new_vote.clone()); @@ -754,7 +755,7 @@ where C: RaftTypeConfig }; debug_assert!( - leader.committed_vote_ref().clone().into_vote() >= *self.state.vote_ref(), + leader.committed_vote_ref().ord_by() >= self.state.vote_ref().ord_by(), "leader.vote({}) >= state.vote({})", leader.committed_vote_ref(), self.state.vote_ref() diff --git a/openraft/src/engine/handler/establish_handler/mod.rs b/openraft/src/engine/handler/establish_handler/mod.rs index 1123012aa..d66b8b369 100644 --- a/openraft/src/engine/handler/establish_handler/mod.rs +++ b/openraft/src/engine/handler/establish_handler/mod.rs @@ -1,9 +1,10 @@ +use crate::base::ord_by::OrdBy; use crate::engine::EngineConfig; use crate::proposer::Candidate; use crate::proposer::Leader; use crate::proposer::LeaderQuorumSet; use crate::proposer::LeaderState; -use crate::vote::RaftLeaderId; +use crate::vote::raft_vote::RaftVoteExt; use crate::RaftTypeConfig; /// Establish a leader for the Engine, when Candidate finishes voting stage. @@ -25,14 +26,14 @@ where C: RaftTypeConfig let vote = candidate.vote_ref().clone(); debug_assert_eq!( - vote.leader_id().node_id(), + vote.leader_node_id(), Some(&self.config.id), "it can only commit its own vote" ); if let Some(l) = self.leader.as_ref() { #[allow(clippy::neg_cmp_op_on_partial_ord)] - if !(vote > l.committed_vote_ref().clone().into_vote()) { + if !(vote.ord_by() > l.committed_vote_ref().ord_by()) { tracing::warn!( "vote is not greater than current existing leader vote. Do not establish new leader and quit" ); diff --git a/openraft/src/engine/handler/following_handler/append_entries_test.rs b/openraft/src/engine/handler/following_handler/append_entries_test.rs index a602d930d..363301d21 100644 --- a/openraft/src/engine/handler/following_handler/append_entries_test.rs +++ b/openraft/src/engine/handler/following_handler/append_entries_test.rs @@ -12,8 +12,10 @@ use crate::raft_state::IOId; use crate::raft_state::LogStateReader; use crate::testing::blank_ent; use crate::testing::log_id; +use crate::type_config::alias::VoteOf; use crate::type_config::TypeConfigExt; use crate::utime::Leased; +use crate::vote::raft_vote::RaftVoteExt; use crate::EffectiveMembership; use crate::Membership; use crate::MembershipState; @@ -28,15 +30,13 @@ fn m23() -> Membership { } fn eng() -> Engine { - let mut eng = Engine::testing_default(0); + let mut eng: Engine = Engine::testing_default(0); eng.state.enable_validation(false); // Disable validation for incomplete state eng.config.id = 2; - eng.state.vote.update( - UTConfig::<()>::now(), - Duration::from_millis(500), - Vote::new_committed(2, 1), - ); + let vote = VoteOf::::new_committed(2, 1); + let now = UTConfig::<()>::now(); + eng.state.vote.update(now, Duration::from_millis(500), vote); eng.state.log_ids.append(log_id(1, 1, 1)); eng.state.log_ids.append(log_id(2, 1, 3)); eng.state.membership_state = MembershipState::new( diff --git a/openraft/src/engine/handler/following_handler/commit_entries_test.rs b/openraft/src/engine/handler/following_handler/commit_entries_test.rs index fd2e8d8dd..f51d0d4b5 100644 --- a/openraft/src/engine/handler/following_handler/commit_entries_test.rs +++ b/openraft/src/engine/handler/following_handler/commit_entries_test.rs @@ -11,6 +11,7 @@ use crate::raft_state::LogStateReader; use crate::testing::log_id; use crate::type_config::TypeConfigExt; use crate::utime::Leased; +use crate::vote::raft_vote::RaftVoteExt; use crate::EffectiveMembership; use crate::Membership; use crate::MembershipState; diff --git a/openraft/src/engine/handler/following_handler/do_append_entries_test.rs b/openraft/src/engine/handler/following_handler/do_append_entries_test.rs index 80fb19ec9..ce9f0b439 100644 --- a/openraft/src/engine/handler/following_handler/do_append_entries_test.rs +++ b/openraft/src/engine/handler/following_handler/do_append_entries_test.rs @@ -13,6 +13,7 @@ use crate::testing::blank_ent; use crate::testing::log_id; use crate::type_config::TypeConfigExt; use crate::utime::Leased; +use crate::vote::raft_vote::RaftVoteExt; use crate::EffectiveMembership; use crate::Entry; use crate::EntryPayload; diff --git a/openraft/src/engine/handler/following_handler/install_snapshot_test.rs b/openraft/src/engine/handler/following_handler/install_snapshot_test.rs index 7edb5758a..acdbc937b 100644 --- a/openraft/src/engine/handler/following_handler/install_snapshot_test.rs +++ b/openraft/src/engine/handler/following_handler/install_snapshot_test.rs @@ -16,7 +16,9 @@ use crate::raft_state::LogStateReader; use crate::storage::Snapshot; use crate::storage::SnapshotMeta; use crate::testing::log_id; +use crate::type_config::alias::VoteOf; use crate::type_config::TypeConfigExt; +use crate::vote::raft_vote::RaftVoteExt; use crate::EffectiveMembership; use crate::Membership; use crate::StoredMembership; @@ -31,14 +33,12 @@ fn m1234() -> Membership { } fn eng() -> Engine { - let mut eng = Engine::testing_default(0); + let mut eng: Engine = Engine::testing_default(0); eng.state.enable_validation(false); // Disable validation for incomplete state - eng.state.vote.update( - UTConfig::<()>::now(), - Duration::from_millis(500), - Vote::new_committed(2, 1), - ); + let now = UTConfig::<()>::now(); + let vote = VoteOf::::new_committed(2, 1); + eng.state.vote.update(now, Duration::from_millis(500), vote); eng.state.committed = Some(log_id(4, 1, 5)); eng.state.log_ids = LogIdList::new(vec![ // diff --git a/openraft/src/engine/handler/leader_handler/append_entries_test.rs b/openraft/src/engine/handler/leader_handler/append_entries_test.rs index 18f6a8408..6a66751ac 100644 --- a/openraft/src/engine/handler/leader_handler/append_entries_test.rs +++ b/openraft/src/engine/handler/leader_handler/append_entries_test.rs @@ -23,6 +23,7 @@ use crate::testing::blank_ent; use crate::testing::log_id; use crate::type_config::TypeConfigExt; use crate::utime::Leased; +use crate::vote::raft_vote::RaftVoteExt; use crate::EffectiveMembership; use crate::Entry; use crate::Membership; diff --git a/openraft/src/engine/handler/leader_handler/send_heartbeat_test.rs b/openraft/src/engine/handler/leader_handler/send_heartbeat_test.rs index ee184ca10..c12814220 100644 --- a/openraft/src/engine/handler/leader_handler/send_heartbeat_test.rs +++ b/openraft/src/engine/handler/leader_handler/send_heartbeat_test.rs @@ -11,6 +11,7 @@ use crate::replication::ReplicationSessionId; use crate::testing::log_id; use crate::type_config::TypeConfigExt; use crate::utime::Leased; +use crate::vote::raft_vote::RaftVoteExt; use crate::EffectiveMembership; use crate::Membership; use crate::MembershipState; diff --git a/openraft/src/engine/handler/replication_handler/mod.rs b/openraft/src/engine/handler/replication_handler/mod.rs index c417d46f7..75b9012d5 100644 --- a/openraft/src/engine/handler/replication_handler/mod.rs +++ b/openraft/src/engine/handler/replication_handler/mod.rs @@ -19,6 +19,7 @@ use crate::raft_state::LogStateReader; use crate::replication::request::Replicate; use crate::replication::response::ReplicationResult; use crate::type_config::alias::InstantOf; +use crate::vote::raft_vote::RaftVoteExt; use crate::EffectiveMembership; use crate::LogId; use crate::LogIdOptionExt; diff --git a/openraft/src/engine/handler/vote_handler/become_leader_test.rs b/openraft/src/engine/handler/vote_handler/become_leader_test.rs index 1fd4ad61b..3d4491fe8 100644 --- a/openraft/src/engine/handler/vote_handler/become_leader_test.rs +++ b/openraft/src/engine/handler/vote_handler/become_leader_test.rs @@ -18,6 +18,7 @@ use crate::testing::log_id; use crate::type_config::alias::EntryOf; use crate::type_config::TypeConfigExt; use crate::utime::Leased; +use crate::vote::raft_vote::RaftVoteExt; use crate::EffectiveMembership; use crate::Membership; use crate::Vote; diff --git a/openraft/src/engine/handler/vote_handler/mod.rs b/openraft/src/engine/handler/vote_handler/mod.rs index 461a06e6a..d54bc1dd1 100644 --- a/openraft/src/engine/handler/vote_handler/mod.rs +++ b/openraft/src/engine/handler/vote_handler/mod.rs @@ -1,6 +1,7 @@ use std::fmt::Debug; use std::time::Duration; +use crate::base::ord_by::OrdBy; use crate::core::raft_msg::ResultSender; use crate::engine::handler::leader_handler::LeaderHandler; use crate::engine::handler::replication_handler::ReplicationHandler; @@ -19,7 +20,9 @@ use crate::raft_state::IOId; use crate::raft_state::LogStateReader; use crate::type_config::alias::VoteOf; use crate::type_config::TypeConfigExt; +use crate::vote::raft_vote::RaftVoteExt; use crate::vote::RaftLeaderId; +use crate::vote::RaftVote; use crate::LogId; use crate::OptionalSend; use crate::RaftState; @@ -103,7 +106,7 @@ where C: RaftTypeConfig // Partial ord compare: // Vote does not have to be total ord. // `!(a >= b)` does not imply `a < b`. - if vote >= self.state.vote_ref() { + if vote.ord_by() >= self.state.vote_ref().ord_by() { // Ok } else { tracing::info!("vote {} is rejected by local vote: {}", vote, self.state.vote_ref()); @@ -123,7 +126,7 @@ where C: RaftTypeConfig Duration::default() }; - if vote > self.state.vote_ref() { + if vote.ord_by() > self.state.vote_ref().ord_by() { tracing::info!("vote is changing from {} to {}", self.state.vote_ref(), vote); self.state.vote.update(C::now(), leader_lease, vote.clone()); @@ -177,7 +180,7 @@ where C: RaftTypeConfig // TODO: this is not gonna happen, // because `self.leader`(previous `internal_server_state`) // does not include Candidate any more. - l.committed_vote = self.state.vote_ref().clone().into_committed(); + l.committed_vote = self.state.vote_ref().to_committed(); self.server_state_handler().update_server_state_if_changed(); return; } @@ -221,7 +224,7 @@ where C: RaftTypeConfig /// This node then becomes raft-follower or raft-learner. pub(crate) fn become_following(&mut self) { debug_assert!( - self.state.vote_ref().leader_id().node_id() != Some(&self.config.id) + self.state.vote_ref().to_leader_id().node_id() != Some(&self.config.id) || !self.state.membership_state.effective().membership().is_voter(&self.config.id), "It must hold: vote is not mine, or I am not a voter(leader just left the cluster)" ); diff --git a/openraft/src/engine/testing.rs b/openraft/src/engine/testing.rs index fab5b3d75..3232ecc45 100644 --- a/openraft/src/engine/testing.rs +++ b/openraft/src/engine/testing.rs @@ -35,9 +35,10 @@ where N: Node + Ord type R = (); type NodeId = u64; type Node = N; - type Entry = crate::Entry; type Term = u64; type LeaderId = crate::impls::leader_id_adv::LeaderId; + type Vote = crate::impls::Vote; + type Entry = crate::Entry; type SnapshotData = Cursor>; type AsyncRuntime = TokioRuntime; type Responder = crate::impls::OneshotResponder; diff --git a/openraft/src/engine/tests/append_entries_test.rs b/openraft/src/engine/tests/append_entries_test.rs index a140b9a9e..f3c440592 100644 --- a/openraft/src/engine/tests/append_entries_test.rs +++ b/openraft/src/engine/tests/append_entries_test.rs @@ -17,6 +17,7 @@ use crate::testing::blank_ent; use crate::testing::log_id; use crate::type_config::TypeConfigExt; use crate::utime::Leased; +use crate::vote::raft_vote::RaftVoteExt; use crate::EffectiveMembership; use crate::Entry; use crate::Membership; diff --git a/openraft/src/engine/tests/handle_vote_resp_test.rs b/openraft/src/engine/tests/handle_vote_resp_test.rs index ce22fee8d..1ec1d628d 100644 --- a/openraft/src/engine/tests/handle_vote_resp_test.rs +++ b/openraft/src/engine/tests/handle_vote_resp_test.rs @@ -20,6 +20,7 @@ use crate::replication::request::Replicate; use crate::testing::log_id; use crate::type_config::TypeConfigExt; use crate::utime::Leased; +use crate::vote::raft_vote::RaftVoteExt; use crate::EffectiveMembership; use crate::Entry; use crate::Membership; diff --git a/openraft/src/engine/tests/initialize_test.rs b/openraft/src/engine/tests/initialize_test.rs index 3911d51ed..bce6ca47f 100644 --- a/openraft/src/engine/tests/initialize_test.rs +++ b/openraft/src/engine/tests/initialize_test.rs @@ -17,6 +17,7 @@ use crate::raft_state::LogStateReader; use crate::testing::log_id; use crate::type_config::TypeConfigExt; use crate::utime::Leased; +use crate::vote::raft_vote::RaftVoteExt; use crate::Entry; use crate::LogId; use crate::Membership; diff --git a/openraft/src/engine/tests/install_full_snapshot_test.rs b/openraft/src/engine/tests/install_full_snapshot_test.rs index 0ed2b7d7d..e86be7b87 100644 --- a/openraft/src/engine/tests/install_full_snapshot_test.rs +++ b/openraft/src/engine/tests/install_full_snapshot_test.rs @@ -17,6 +17,7 @@ use crate::storage::Snapshot; use crate::storage::SnapshotMeta; use crate::testing::log_id; use crate::type_config::TypeConfigExt; +use crate::vote::raft_vote::RaftVoteExt; use crate::Membership; use crate::StoredMembership; use crate::Vote; @@ -30,7 +31,7 @@ fn m1234() -> Membership { } fn eng() -> Engine { - let mut eng = Engine::testing_default(0); + let mut eng: Engine = Engine::testing_default(0); eng.state.enable_validation(false); // Disable validation for incomplete state eng.state.vote.update( diff --git a/openraft/src/engine/tests/startup_test.rs b/openraft/src/engine/tests/startup_test.rs index 4c52a0fd4..7c9a3621a 100644 --- a/openraft/src/engine/tests/startup_test.rs +++ b/openraft/src/engine/tests/startup_test.rs @@ -18,6 +18,7 @@ use crate::replication::request::Replicate; use crate::testing::log_id; use crate::type_config::TypeConfigExt; use crate::utime::Leased; +use crate::vote::raft_vote::RaftVoteExt; use crate::EffectiveMembership; use crate::Entry; use crate::Membership; diff --git a/openraft/src/impls/mod.rs b/openraft/src/impls/mod.rs index 950a756de..514640898 100644 --- a/openraft/src/impls/mod.rs +++ b/openraft/src/impls/mod.rs @@ -16,3 +16,11 @@ pub mod leader_id_adv { pub mod leader_id_std { pub use crate::vote::leader_id::leader_id_std::LeaderId; } + +/// Default [`RaftVote`] implementation for both standard Raft mode and multi-leader-per-term mode. +/// +/// The difference between the two modes is the implementation of [`RaftLeaderId`]. +/// +/// [`RaftVote`]: crate::vote::raft_vote::RaftVote +/// [`RaftLeaderId`]: crate::vote::RaftLeaderId +pub use crate::vote::Vote; diff --git a/openraft/src/metrics/metric.rs b/openraft/src/metrics/metric.rs index d1cb79fd8..9081e0b9f 100644 --- a/openraft/src/metrics/metric.rs +++ b/openraft/src/metrics/metric.rs @@ -1,5 +1,6 @@ use std::cmp::Ordering; +use crate::base::ord_by::OrdBy; use crate::metrics::metric_display::MetricDisplay; use crate::type_config::alias::VoteOf; use crate::LogId; @@ -67,7 +68,7 @@ where C: RaftTypeConfig fn partial_cmp(&self, other: &Metric) -> Option { match other { Metric::Term(v) => Some(self.current_term.cmp(v)), - Metric::Vote(v) => self.vote.partial_cmp(v), + Metric::Vote(v) => self.vote.ord_by().partial_cmp(&v.ord_by()), Metric::LastLogIndex(v) => Some(self.last_log_index.cmp(v)), Metric::Applied(v) => Some(self.last_applied.cmp(v)), Metric::AppliedIndex(v) => Some(self.last_applied.index().cmp(v)), diff --git a/openraft/src/metrics/raft_metrics.rs b/openraft/src/metrics/raft_metrics.rs index 929bb545c..cbe18aa71 100644 --- a/openraft/src/metrics/raft_metrics.rs +++ b/openraft/src/metrics/raft_metrics.rs @@ -15,7 +15,6 @@ use crate::Instant; use crate::LogId; use crate::RaftTypeConfig; use crate::StoredMembership; -use crate::Vote; /// A set of metrics describing the current state of a Raft node. #[derive(Clone, Debug, PartialEq, Eq)] @@ -169,7 +168,7 @@ where C: RaftTypeConfig id, current_term: Default::default(), - vote: Vote::default(), + vote: Default::default(), last_log_index: None, last_applied: None, snapshot: None, diff --git a/openraft/src/metrics/wait_test.rs b/openraft/src/metrics/wait_test.rs index 83d291954..e8ad778fe 100644 --- a/openraft/src/metrics/wait_test.rs +++ b/openraft/src/metrics/wait_test.rs @@ -252,7 +252,7 @@ where C: RaftTypeConfig { id: NodeIdOf::::default(), state: ServerState::Learner, current_term: Default::default(), - vote: Vote::default(), + vote: Default::default(), last_log_index: None, last_applied: None, purged: None, diff --git a/openraft/src/network/snapshot_transport.rs b/openraft/src/network/snapshot_transport.rs index 720eb8385..e460c74cf 100644 --- a/openraft/src/network/snapshot_transport.rs +++ b/openraft/src/network/snapshot_transport.rs @@ -18,6 +18,7 @@ mod tokio_rt { use super::Chunked; use super::SnapshotTransport; use super::Streaming; + use crate::base::ord_by::OrdBy; use crate::error::Fatal; use crate::error::InstallSnapshotError; use crate::error::RPCError; @@ -148,7 +149,7 @@ mod tokio_rt { } }; - if resp.vote > vote { + if resp.vote.ord_by() > vote.ord_by() { // Unfinished, return a response with a higher vote. // The caller checks the vote and return a HigherVote error. return Ok(SnapshotResponse::new(resp.vote)); diff --git a/openraft/src/proposer/candidate.rs b/openraft/src/proposer/candidate.rs index 4b1582276..493ad6b81 100644 --- a/openraft/src/proposer/candidate.rs +++ b/openraft/src/proposer/candidate.rs @@ -10,6 +10,8 @@ use crate::quorum::QuorumSet; use crate::type_config::alias::InstantOf; use crate::type_config::alias::LogIdOf; use crate::type_config::alias::VoteOf; +use crate::vote::raft_vote::RaftVoteExt; +use crate::vote::RaftVote; use crate::LogId; use crate::RaftTypeConfig; diff --git a/openraft/src/proposer/leader.rs b/openraft/src/proposer/leader.rs index 107be2a47..886316206 100644 --- a/openraft/src/proposer/leader.rs +++ b/openraft/src/proposer/leader.rs @@ -10,7 +10,7 @@ use crate::type_config::alias::InstantOf; use crate::type_config::alias::LogIdOf; use crate::type_config::TypeConfigExt; use crate::vote::committed::CommittedVote; -use crate::vote::RaftLeaderId; +use crate::vote::raft_vote::RaftVoteExt; use crate::LogId; use crate::LogIdOptionExt; use crate::RaftLogId; @@ -203,7 +203,7 @@ where // Thus vote.voted_for() is this node. // Safe unwrap: voted_for() is always non-None in Openraft - let node_id = self.committed_vote.clone().into_vote().leader_id().node_id().cloned().unwrap(); + let node_id = self.committed_vote.to_leader_node_id().unwrap(); let now = C::now(); tracing::debug!( @@ -233,6 +233,7 @@ mod tests { use crate::testing::blank_ent; use crate::testing::log_id; use crate::type_config::TypeConfigExt; + use crate::vote::raft_vote::RaftVoteExt; use crate::Entry; use crate::RaftLogId; use crate::Vote; diff --git a/openraft/src/raft/declare_raft_types_test.rs b/openraft/src/raft/declare_raft_types_test.rs index aeece573e..6c95fdd94 100644 --- a/openraft/src/raft/declare_raft_types_test.rs +++ b/openraft/src/raft/declare_raft_types_test.rs @@ -20,6 +20,7 @@ declare_raft_types!( Term = u64, LeaderId = crate::impls::leader_id_std::LeaderId, Entry = crate::Entry, + Vote = crate::impls::Vote, SnapshotData = Cursor>, AsyncRuntime = TokioRuntime, Responder = crate::impls::OneshotResponder, diff --git a/openraft/src/raft/mod.rs b/openraft/src/raft/mod.rs index 77824c59f..b866c7bc0 100644 --- a/openraft/src/raft/mod.rs +++ b/openraft/src/raft/mod.rs @@ -46,6 +46,7 @@ use tracing::Level; use crate::async_runtime::watch::WatchReceiver; use crate::async_runtime::MpscUnboundedSender; use crate::async_runtime::OneshotSender; +use crate::base::ord_by::OrdBy; use crate::base::BoxAsyncOnceMut; use crate::base::BoxFuture; use crate::base::BoxOnce; @@ -116,6 +117,7 @@ use crate::StorageHelper; /// Node = openraft::BasicNode, /// Term = u64, /// LeaderId = openraft::impls::leader_id_adv::LeaderId, +/// Vote = openraft::impls::Vote, /// Entry = openraft::Entry, /// SnapshotData = Cursor>, /// Responder = openraft::impls::OneshotResponder, @@ -130,6 +132,7 @@ use crate::StorageHelper; /// - `Node`: `::openraft::impls::BasicNode` /// - `Term`: `u64` /// - `LeaderId`: `::openraft::impls::leader_id_adv::LeaderId` +/// - `Vote`: `::openraft::impls::Vote` /// - `Entry`: `::openraft::impls::Entry` /// - `SnapshotData`: `Cursor>` /// - `Responder`: `::openraft::impls::OneshotResponder` @@ -178,6 +181,7 @@ macro_rules! declare_raft_types { (Node , , $crate::impls::BasicNode ), (Term , , u64 ), (LeaderId , , $crate::impls::leader_id_adv::LeaderId ), + (Vote , , $crate::impls::Vote ), (Entry , , $crate::impls::Entry ), (SnapshotData , , std::io::Cursor> ), (Responder , , $crate::impls::OneshotResponder ), @@ -468,7 +472,7 @@ where C: RaftTypeConfig // It is not mandatory because it is just a read operation // but prevent unnecessary snapshot transfer early. { - if req_vote >= my_vote { + if req_vote.ord_by() >= my_vote.ord_by() { // Ok } else { tracing::info!("vote {} is rejected by local vote: {}", req_vote, my_vote); @@ -687,7 +691,7 @@ where C: RaftTypeConfig // Condition failed to become Leader #[allow(clippy::neg_cmp_op_on_partial_ord)] - let fail = |m: &RaftMetrics| !(req.from_leader >= m.vote); + let fail = |m: &RaftMetrics| !(req.from_leader.ord_by() >= m.vote.ord_by()); let timeout = Some(Duration::from_millis(self.inner.config.election_timeout_min)); let metrics_res = diff --git a/openraft/src/raft_state/io_state/io_id.rs b/openraft/src/raft_state/io_state/io_id.rs index 93d4e9bd0..d2fa43c96 100644 --- a/openraft/src/raft_state/io_state/io_id.rs +++ b/openraft/src/raft_state/io_state/io_id.rs @@ -5,7 +5,9 @@ use crate::raft_state::io_state::log_io_id::LogIOId; use crate::type_config::alias::VoteOf; use crate::vote::committed::CommittedVote; use crate::vote::non_committed::NonCommittedVote; +use crate::vote::raft_vote::RaftVoteExt; use crate::vote::ref_vote::RefVote; +use crate::vote::RaftVote; use crate::ErrorSubject; use crate::ErrorVerb; use crate::LogId; @@ -70,9 +72,9 @@ where C: RaftTypeConfig { pub(crate) fn new(vote: &VoteOf) -> Self { if vote.is_committed() { - Self::new_log_io(vote.clone().into_committed(), None) + Self::new_log_io(vote.to_committed(), None) } else { - Self::new_vote_io(vote.clone().into_non_committed()) + Self::new_vote_io(vote.to_non_committed()) } } diff --git a/openraft/src/raft_state/mod.rs b/openraft/src/raft_state/mod.rs index 8cf870907..05346f754 100644 --- a/openraft/src/raft_state/mod.rs +++ b/openraft/src/raft_state/mod.rs @@ -13,7 +13,6 @@ use crate::LogId; use crate::LogIdOptionExt; use crate::RaftTypeConfig; use crate::ServerState; -use crate::Vote; pub(crate) mod io_state; mod log_state_reader; @@ -36,13 +35,16 @@ pub(crate) use log_state_reader::LogStateReader; pub use membership_state::MembershipState; pub(crate) use vote_state_reader::VoteStateReader; +use crate::base::ord_by::OrdBy; use crate::display_ext::DisplayOptionExt; use crate::proposer::Leader; use crate::proposer::LeaderQuorumSet; use crate::type_config::alias::InstantOf; use crate::type_config::alias::LogIdOf; use crate::type_config::alias::VoteOf; +use crate::vote::raft_vote::RaftVoteExt; use crate::vote::RaftLeaderId; +use crate::vote::RaftVote; /// A struct used to represent the raft state which a Raft node needs. #[derive(Clone, Debug)] @@ -206,7 +208,7 @@ where C: RaftTypeConfig } // If it received a request-vote from other node, it is already initialized. - if self.vote_ref() != &Vote::default() { + if self.vote_ref() != &VoteOf::::default() { return true; } @@ -237,7 +239,7 @@ where C: RaftTypeConfig let new_vote = accepted.to_vote(); let current_vote = curr_accepted.clone().map(|io_id| io_id.to_vote()); assert!( - Some(&new_vote) >= current_vote.as_ref(), + Some(new_vote.ord_by()) >= current_vote.as_ref().map(|x| x.ord_by()), "new accepted.committed_vote {} must be >= current accepted.committed_vote: {}", new_vote, current_vote.display(), @@ -369,7 +371,7 @@ where C: RaftTypeConfig /// /// [Determine Server State]: crate::docs::data::vote#vote-and-membership-define-the-server-state pub(crate) fn is_leading(&self, id: &C::NodeId) -> bool { - self.membership_state.contains(id) && self.vote.leader_id().node_id() == Some(id) + self.membership_state.contains(id) && self.vote.leader_node_id() == Some(id) } /// The node is leader @@ -396,7 +398,7 @@ where C: RaftTypeConfig let last_leader_log_ids = self.log_ids.by_last_leader(); Leader::new( - self.vote_ref().clone().into_committed(), + self.vote_ref().to_committed(), em.to_quorum_set(), em.learner_ids(), last_leader_log_ids, @@ -409,7 +411,7 @@ where C: RaftTypeConfig if vote.is_committed() { // Safe unwrap(): vote that is committed has to already have voted for some node. - let id = vote.leader_id().node_id().cloned().unwrap(); + let id = vote.to_leader_id().node_id().cloned().unwrap(); return self.new_forward_to_leader(id); } diff --git a/openraft/src/replication/mod.rs b/openraft/src/replication/mod.rs index 81c9d17d5..55b90f23c 100644 --- a/openraft/src/replication/mod.rs +++ b/openraft/src/replication/mod.rs @@ -21,6 +21,7 @@ use tracing_futures::Instrument; use crate::async_runtime::MpscUnboundedReceiver; use crate::async_runtime::MpscUnboundedSender; use crate::async_runtime::MpscUnboundedWeakSender; +use crate::base::ord_by::OrdBy; use crate::config::Config; use crate::core::notification::Notification; use crate::core::sm::handle::SnapshotReader; @@ -57,7 +58,7 @@ use crate::type_config::alias::OneshotSenderOf; use crate::type_config::alias::VoteOf; use crate::type_config::async_runtime::mutex::Mutex; use crate::type_config::TypeConfigExt; -use crate::vote::RaftLeaderId; +use crate::vote::raft_vote::RaftVoteExt; use crate::LogId; use crate::RaftLogId; use crate::RaftNetworkFactory; @@ -443,7 +444,7 @@ where let append_res = res.map_err(|_e| { let to = Timeout { action: RPCTypes::AppendEntries, - id: self.session_id.vote().leader_id().node_id().cloned().unwrap(), + id: self.session_id.vote().to_leader_node_id().unwrap(), target: self.target.clone(), timeout: the_timeout, }; @@ -484,7 +485,7 @@ where } AppendEntriesResponse::HigherVote(vote) => { debug_assert!( - vote > self.session_id.vote(), + vote.ord_by() > self.session_id.vote().ord_by(), "higher vote({}) should be greater than leader's vote({})", vote, self.session_id.vote(), @@ -803,7 +804,7 @@ where // Handle response conditions. let sender_vote = self.session_id.vote(); - if resp.vote > sender_vote { + if resp.vote.ord_by() > sender_vote.ord_by() { return Err(ReplicationError::HigherVote(HigherVote { higher: resp.vote, sender_vote, diff --git a/openraft/src/storage/v2/raft_log_storage_ext.rs b/openraft/src/storage/v2/raft_log_storage_ext.rs index 5ce52bdd0..c13d4cd07 100644 --- a/openraft/src/storage/v2/raft_log_storage_ext.rs +++ b/openraft/src/storage/v2/raft_log_storage_ext.rs @@ -9,6 +9,7 @@ use crate::storage::IOFlushed; use crate::storage::RaftLogStorage; use crate::type_config::alias::VoteOf; use crate::type_config::TypeConfigExt; +use crate::vote::raft_vote::RaftVoteExt; use crate::OptionalSend; use crate::RaftTypeConfig; use crate::StorageError; diff --git a/openraft/src/testing/log/suite.rs b/openraft/src/testing/log/suite.rs index 21fd00f2f..fb4ea693b 100644 --- a/openraft/src/testing/log/suite.rs +++ b/openraft/src/testing/log/suite.rs @@ -25,6 +25,7 @@ use crate::storage::StorageHelper; use crate::testing::log::StoreBuilder; use crate::type_config::alias::VoteOf; use crate::type_config::TypeConfigExt; +use crate::vote::raft_vote::RaftVoteExt; use crate::vote::RaftLeaderIdExt; use crate::LogId; use crate::Membership; @@ -34,7 +35,6 @@ use crate::RaftSnapshotBuilder; use crate::RaftTypeConfig; use crate::StorageError; use crate::StoredMembership; -use crate::Vote; const NODE_ID: u64 = 0; @@ -466,11 +466,11 @@ where want.vote.update( initial.vote.last_update().unwrap(), Duration::default(), - Vote::default(), + VoteOf::::default(), ); - want.io_state.io_progress.accept(IOId::new(&Vote::default())); - want.io_state.io_progress.submit(IOId::new(&Vote::default())); - want.io_state.io_progress.flush(IOId::new(&Vote::default())); + want.io_state.io_progress.accept(IOId::new(&VoteOf::::default())); + want.io_state.io_progress.submit(IOId::new(&VoteOf::::default())); + want.io_state.io_progress.flush(IOId::new(&VoteOf::::default())); assert_eq!(want, initial, "uninitialized state"); Ok(()) @@ -501,7 +501,7 @@ where "unexpected value for last applied log" ); assert_eq!( - Vote::new(1u64.into(), NODE_ID.into()), + VoteOf::::from_term_node_id(1u64.into(), NODE_ID.into()), *initial.vote_ref(), "unexpected value for default hard state" ); @@ -822,11 +822,11 @@ where } pub async fn save_vote(mut store: LS, mut sm: SM) -> Result<(), StorageError> { - store.save_vote(&Vote::new(100.into(), NODE_ID.into())).await?; + store.save_vote(&VoteOf::::from_term_node_id(100.into(), NODE_ID.into())).await?; let got = store.read_vote().await?; - assert_eq!(Some(Vote::new(100.into(), NODE_ID.into())), got,); + assert_eq!(Some(VoteOf::::from_term_node_id(100.into(), NODE_ID.into())), got,); Ok(()) } @@ -1367,7 +1367,7 @@ where } pub async fn default_vote(sto: &mut LS) -> Result<(), StorageError> { - sto.save_vote(&Vote::new(1u64.into(), NODE_ID.into())).await?; + sto.save_vote(&VoteOf::::from_term_node_id(1u64.into(), NODE_ID.into())).await?; Ok(()) } diff --git a/openraft/src/type_config.rs b/openraft/src/type_config.rs index 00e5ce3f9..c95f4df53 100644 --- a/openraft/src/type_config.rs +++ b/openraft/src/type_config.rs @@ -16,6 +16,7 @@ pub use util::TypeConfigExt; use crate::entry::FromAppData; use crate::entry::RaftEntry; use crate::raft::responder::Responder; +use crate::vote::raft_vote::RaftVote; use crate::vote::RaftLeaderId; use crate::vote::RaftTerm; use crate::AppData; @@ -47,6 +48,7 @@ use crate::OptionalSync; /// Node = openraft::BasicNode, /// Term = u64, /// LeaderId = openraft::impls::leader_id_adv::LeaderId, +/// Vote = openraft::impls::Vote, /// Entry = openraft::impls::Entry, /// SnapshotData = Cursor>, /// AsyncRuntime = openraft::TokioRuntime, @@ -81,6 +83,11 @@ pub trait RaftTypeConfig: /// A Leader identifier in a cluster. type LeaderId: RaftLeaderId; + /// Raft vote type. + /// + /// It represents a candidate's vote or a leader's vote that has been granted by a quorum. + type Vote: RaftVote; + /// Raft log entry, which can be built from an AppData. type Entry: RaftEntry + FromAppData; @@ -126,6 +133,7 @@ pub mod alias { pub type NodeOf = ::Node; pub type TermOf = ::Term; pub type LeaderIdOf = ::LeaderId; + pub type VoteOf = ::Vote; pub type EntryOf = ::Entry; pub type SnapshotDataOf = ::SnapshotData; pub type AsyncRuntimeOf = ::AsyncRuntime; @@ -172,7 +180,6 @@ pub mod alias { // Usually used types pub type LogIdOf = crate::LogId; - pub type VoteOf = crate::Vote; pub type CommittedLeaderIdOf = as RaftLeaderId>::Committed; pub type SerdeInstantOf = crate::metrics::SerdeInstant>; } diff --git a/openraft/src/vote/committed.rs b/openraft/src/vote/committed.rs index 07f4745f2..9bd07ac7e 100644 --- a/openraft/src/vote/committed.rs +++ b/openraft/src/vote/committed.rs @@ -2,21 +2,24 @@ use std::cmp::Ordering; use std::fmt; use crate::type_config::alias::CommittedLeaderIdOf; +use crate::type_config::alias::LeaderIdOf; use crate::type_config::alias::VoteOf; -use crate::vote::ref_vote::RefVote; +use crate::vote::raft_vote::RaftVoteExt; use crate::vote::RaftLeaderId; +use crate::vote::RaftVote; use crate::RaftTypeConfig; /// Represents a committed Vote that has been accepted by a quorum. /// /// The inner `Vote`'s attribute `committed` is always set to `true` -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] #[derive(PartialEq, Eq)] #[derive(PartialOrd)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound = ""))] pub(crate) struct CommittedVote where C: RaftTypeConfig { - vote: VoteOf, + leader_id: LeaderIdOf, } /// The `CommittedVote` is totally ordered. @@ -30,28 +33,23 @@ impl Ord for CommittedVote where C: RaftTypeConfig { fn cmp(&self, other: &Self) -> Ordering { - self.vote.partial_cmp(&other.vote).unwrap() + self.as_ref_vote().partial_cmp(&other.as_ref_vote()).unwrap() } } impl CommittedVote where C: RaftTypeConfig { - pub(crate) fn new(mut vote: VoteOf) -> Self { - vote.committed = true; - Self { vote } + pub(crate) fn new(leader_id: LeaderIdOf) -> Self { + Self { leader_id } } pub(crate) fn committed_leader_id(&self) -> CommittedLeaderIdOf { - self.vote.leader_id().to_committed() + self.leader_id().map_or_else(Default::default, RaftLeaderId::to_committed) } pub(crate) fn into_vote(self) -> VoteOf { - self.vote - } - - pub(crate) fn as_ref_vote(&self) -> RefVote<'_, C> { - RefVote::new(&self.vote.leader_id, true) + VoteOf::::from_leader_id(self.leader_id, true) } } @@ -59,6 +57,22 @@ impl fmt::Display for CommittedVote where C: RaftTypeConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.vote.fmt(f) + self.as_ref_vote().fmt(f) + } +} + +impl RaftVote for CommittedVote +where C: RaftTypeConfig +{ + fn from_leader_id(_leader_id: C::LeaderId, _committed: bool) -> Self { + unimplemented!() + } + + fn leader_id(&self) -> Option<&LeaderIdOf> { + Some(&self.leader_id) + } + + fn is_committed(&self) -> bool { + true } } diff --git a/openraft/src/vote/mod.rs b/openraft/src/vote/mod.rs index 0d094f90d..e11fbda34 100644 --- a/openraft/src/vote/mod.rs +++ b/openraft/src/vote/mod.rs @@ -3,17 +3,18 @@ pub(crate) mod committed; pub(crate) mod leader_id; pub(crate) mod non_committed; +pub(crate) mod raft_term; +pub(crate) mod raft_vote; pub(crate) mod ref_vote; #[allow(clippy::module_inception)] mod vote; pub(crate) mod vote_status; -pub(crate) mod raft_term; - pub use leader_id::raft_committed_leader_id::RaftCommittedLeaderId; pub use leader_id::raft_leader_id::RaftLeaderId; pub use leader_id::raft_leader_id::RaftLeaderIdExt; pub use raft_term::RaftTerm; +pub use raft_vote::RaftVote; pub use self::leader_id::leader_id_adv; pub use self::leader_id::leader_id_cmp::LeaderIdCompare; diff --git a/openraft/src/vote/non_committed.rs b/openraft/src/vote/non_committed.rs index 06195fd6e..4003d12d1 100644 --- a/openraft/src/vote/non_committed.rs +++ b/openraft/src/vote/non_committed.rs @@ -2,39 +2,48 @@ use std::fmt; use crate::type_config::alias::LeaderIdOf; use crate::type_config::alias::VoteOf; -use crate::vote::ref_vote::RefVote; +use crate::vote::raft_vote::RaftVoteExt; +use crate::vote::RaftVote; use crate::RaftTypeConfig; /// Represents a non-committed Vote that has **NOT** been granted by a quorum. /// /// The inner `Vote`'s attribute `committed` is always set to `false` -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] #[derive(PartialEq, Eq)] #[derive(PartialOrd)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound = ""))] pub(crate) struct NonCommittedVote where C: RaftTypeConfig { - vote: VoteOf, + leader_id: LeaderIdOf, } impl NonCommittedVote where C: RaftTypeConfig { - pub(crate) fn new(vote: VoteOf) -> Self { - debug_assert!(!vote.committed); - Self { vote } + pub(crate) fn new(leader_id: LeaderIdOf) -> Self { + Self { leader_id } } - pub(crate) fn leader_id(&self) -> &LeaderIdOf { - &self.vote.leader_id + pub(crate) fn into_vote(self) -> VoteOf { + VoteOf::::from_leader_id(self.leader_id, false) } +} - pub(crate) fn into_vote(self) -> VoteOf { - self.vote +impl RaftVote for NonCommittedVote +where C: RaftTypeConfig +{ + fn from_leader_id(_leader_id: C::LeaderId, _committed: bool) -> Self { + unimplemented!() + } + + fn leader_id(&self) -> Option<&LeaderIdOf> { + Some(&self.leader_id) } - pub(crate) fn as_ref_vote(&self) -> RefVote<'_, C> { - RefVote::new(&self.vote.leader_id, false) + fn is_committed(&self) -> bool { + false } } @@ -42,6 +51,6 @@ impl fmt::Display for NonCommittedVote where C: RaftTypeConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.vote.fmt(f) + self.as_ref_vote().fmt(f) } } diff --git a/openraft/src/vote/raft_vote.rs b/openraft/src/vote/raft_vote.rs new file mode 100644 index 000000000..f88cf45ec --- /dev/null +++ b/openraft/src/vote/raft_vote.rs @@ -0,0 +1,123 @@ +use std::fmt::Debug; +use std::fmt::Display; + +use crate::base::ord_by::OrdBy; +use crate::base::OptionalFeatures; +use crate::type_config::alias::CommittedLeaderIdOf; +use crate::vote::committed::CommittedVote; +use crate::vote::non_committed::NonCommittedVote; +use crate::vote::ref_vote::RefVote; +use crate::vote::vote_status::VoteStatus; +use crate::vote::RaftLeaderId; +use crate::RaftTypeConfig; +// TODO: OptionSerde can be removed after all types are made trait based. + +/// Represents a vote in Raft consensus, including both votes for leader candidates +/// and committed leader(a leader granted by a quorum). +pub trait RaftVote +where + C: RaftTypeConfig, + Self: OptionalFeatures + Eq + Clone + Debug + Display + Default + 'static, +{ + /// Create a new vote for the specified leader with optional quorum commitment + fn from_leader_id(leader_id: C::LeaderId, committed: bool) -> Self; + + /// Get a reference to this vote's LeaderId([`RaftLeaderId`] implementation) + fn leader_id(&self) -> Option<&C::LeaderId>; + + /// Whether this vote has been committed by a quorum + fn is_committed(&self) -> bool; +} + +pub(crate) trait RaftVoteExt +where + C: RaftTypeConfig, + Self: RaftVote, +{ + /// Creates a new vote for a node in a specific term, with uncommitted status. + fn from_term_node_id(term: C::Term, node_id: C::NodeId) -> Self { + let leader_id = C::LeaderId::new(term, node_id); + Self::from_leader_id(leader_id, false) + } + + fn term(&self) -> C::Term { + self.leader_id().map_or_else(C::Term::default, RaftLeaderId::term) + } + + /// Gets the node ID of the leader this vote is for, if present. + fn to_leader_node_id(&self) -> Option { + self.leader_node_id().cloned() + } + + /// Gets a reference to the node ID of the leader this vote is for, if present. + fn leader_node_id(&self) -> Option<&C::NodeId> { + self.leader_id().and_then(|leader_id| leader_id.node_id()) + } + + /// Gets the leader ID this vote is associated with. + fn to_leader_id(&self) -> C::LeaderId { + self.leader_id().cloned().unwrap_or_default() + } + + /// Creates a reference view of this vote. + /// + /// Returns a lightweight `RefVote` that borrows the data from this vote. + fn as_ref_vote(&self) -> RefVote<'_, C> { + RefVote::new(self.leader_id(), self.is_committed()) + } + + /// Create a [`CommittedVote`] with the same leader id. + fn to_committed(&self) -> CommittedVote { + CommittedVote::new(self.to_leader_id()) + } + + /// Create a [`NonCommittedVote`] with the same leader id. + fn to_non_committed(&self) -> NonCommittedVote { + NonCommittedVote::new(self.to_leader_id()) + } + + /// Convert this vote into a [`CommittedVote`] + fn into_committed(self) -> CommittedVote { + CommittedVote::new(self.to_leader_id()) + } + + /// Convert this vote into a [`NonCommittedVote`] + fn into_non_committed(self) -> NonCommittedVote { + NonCommittedVote::new(self.to_leader_id()) + } + + /// Converts this vote into a [`VoteStatus`] enum based on its commitment state. + fn into_vote_status(self) -> VoteStatus { + if self.is_committed() { + VoteStatus::Committed(self.into_committed()) + } else { + VoteStatus::Pending(self.into_non_committed()) + } + } + + /// Checks if this vote is for the same leader as specified by the given committed leader ID. + fn is_same_leader(&self, leader_id: &CommittedLeaderIdOf) -> bool { + self.leader_id().map(|x| x.to_committed()).unwrap_or_default() == *leader_id + } +} + +impl RaftVoteExt for T +where + C: RaftTypeConfig, + T: RaftVote, +{ +} + +impl OrdBy for T +where + C: RaftTypeConfig, + T: RaftVote, +{ + type By<'k> + = RefVote<'k, C> + where Self: 'k; + + fn ord_by(&self) -> Self::By<'_> { + self.as_ref_vote() + } +} diff --git a/openraft/src/vote/ref_vote.rs b/openraft/src/vote/ref_vote.rs index 7d7c2a9af..ef454ba16 100644 --- a/openraft/src/vote/ref_vote.rs +++ b/openraft/src/vote/ref_vote.rs @@ -1,23 +1,25 @@ use std::cmp::Ordering; use std::fmt::Formatter; +use crate::display_ext::DisplayOptionExt; use crate::RaftTypeConfig; -/// Same as [`Vote`] but with a reference to the `LeaderId`. +/// Similar to [`Vote`] but with a reference to the `LeaderId`, and provide ordering and display +/// implementation. /// /// [`Vote`]: crate::vote::Vote #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) struct RefVote<'a, C> where C: RaftTypeConfig { - pub(crate) leader_id: &'a C::LeaderId, + pub(crate) leader_id: Option<&'a C::LeaderId>, pub(crate) committed: bool, } impl<'a, C> RefVote<'a, C> where C: RaftTypeConfig { - pub(crate) fn new(leader_id: &'a C::LeaderId, committed: bool) -> Self { + pub(crate) fn new(leader_id: Option<&'a C::LeaderId>, committed: bool) -> Self { Self { leader_id, committed } } @@ -32,7 +34,7 @@ where C: RaftTypeConfig { #[inline] fn partial_cmp(&self, other: &RefVote<'a, C>) -> Option { - match PartialOrd::partial_cmp(self.leader_id, other.leader_id) { + match PartialOrd::partial_cmp(&self.leader_id, &other.leader_id) { Some(Ordering::Equal) => PartialOrd::partial_cmp(&self.committed, &other.committed), None => { // If two leader_id are not comparable, they won't both be granted(committed). @@ -59,7 +61,7 @@ where C: RaftTypeConfig write!( f, "<{}:{}>", - self.leader_id, + self.leader_id.display(), if self.is_committed() { "Q" } else { "-" } ) } diff --git a/openraft/src/vote/vote.rs b/openraft/src/vote/vote.rs index 37a667e1f..79c61bf42 100644 --- a/openraft/src/vote/vote.rs +++ b/openraft/src/vote/vote.rs @@ -1,16 +1,13 @@ use std::cmp::Ordering; use std::fmt::Formatter; -use crate::type_config::alias::CommittedLeaderIdOf; -use crate::vote::committed::CommittedVote; -use crate::vote::non_committed::NonCommittedVote; -use crate::vote::ref_vote::RefVote; -use crate::vote::vote_status::VoteStatus; +use crate::vote::raft_vote::RaftVoteExt; use crate::vote::RaftLeaderId; +use crate::vote::RaftVote; use crate::RaftTypeConfig; /// `Vote` represent the privilege of a node. -#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound = ""))] pub struct Vote { /// The id of the node that tries to become the leader. @@ -19,13 +16,6 @@ pub struct Vote { pub committed: bool, } -impl Copy for Vote -where - C: RaftTypeConfig, - C::LeaderId: Copy, -{ -} - impl PartialOrd for Vote where C: RaftTypeConfig { @@ -39,12 +29,23 @@ impl std::fmt::Display for Vote where C: RaftTypeConfig { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "<{}:{}>", - self.leader_id, - if self.is_committed() { "Q" } else { "-" } - ) + self.as_ref_vote().fmt(f) + } +} + +impl RaftVote for Vote +where C: RaftTypeConfig +{ + fn from_leader_id(leader_id: C::LeaderId, committed: bool) -> Self { + Self { leader_id, committed } + } + + fn leader_id(&self) -> Option<&C::LeaderId> { + Some(&self.leader_id) + } + + fn is_committed(&self) -> bool { + self.committed } } @@ -70,27 +71,6 @@ where C: RaftTypeConfig self.committed = true } - pub(crate) fn as_ref_vote(&self) -> RefVote<'_, C> { - RefVote::new(&self.leader_id, self.committed) - } - - /// Convert this vote into a `CommittedVote` - pub(crate) fn into_committed(self) -> CommittedVote { - CommittedVote::new(self) - } - - pub(crate) fn into_non_committed(self) -> NonCommittedVote { - NonCommittedVote::new(self) - } - - pub(crate) fn into_vote_status(self) -> VoteStatus { - if self.committed { - VoteStatus::Committed(self.into_committed()) - } else { - VoteStatus::Pending(self.into_non_committed()) - } - } - pub fn is_committed(&self) -> bool { self.committed } @@ -101,11 +81,6 @@ where C: RaftTypeConfig pub fn leader_id(&self) -> &C::LeaderId { &self.leader_id } - - // TODO: remove this method - pub(crate) fn is_same_leader(&self, leader_id: &CommittedLeaderIdOf) -> bool { - self.leader_id().to_committed() == *leader_id - } } #[cfg(test)] diff --git a/tests/tests/fixtures/mod.rs b/tests/tests/fixtures/mod.rs index 9c7fce9f4..640ee4e52 100644 --- a/tests/tests/fixtures/mod.rs +++ b/tests/tests/fixtures/mod.rs @@ -185,9 +185,11 @@ impl fmt::Display for Direction { } } +use openraft::alias::VoteOf; use openraft::network::v2::RaftNetworkV2; use openraft::vote::RaftLeaderId; use openraft::vote::RaftLeaderIdExt; +use openraft::vote::RaftVote; use Direction::NetRecv; use Direction::NetSend; @@ -416,7 +418,7 @@ impl TypedRaftRouter { tracing::info!(log_index, "--- wait for init node to become leader"); self.wait_for_log(&btreeset![leader_id], Some(log_index), timeout(), "init").await?; - self.wait(&leader_id, timeout()).vote(Vote::new_committed(1, 0), "init vote").await?; + self.wait(&leader_id, timeout()).vote(VoteOf::::new_committed(1, 0), "init vote").await?; for id in voter_ids.iter() { if *id == leader_id { @@ -892,7 +894,7 @@ impl TypedRaftRouter { if let Some(voted_for) = &expect_voted_for { assert_eq!( - vote.leader_id().node_id(), + vote.leader_node_id(), Some(voted_for), "expected node {} to have voted for {}, got {:?}", id, @@ -1022,7 +1024,7 @@ impl RaftNetworkV2 for RaftRouterNetwork { mut rpc: AppendEntriesRequest, _option: RPCOption, ) -> Result, RPCError> { - let from_id = rpc.vote.leader_id().node_id().cloned().unwrap(); + let from_id = rpc.vote.to_leader_node_id().unwrap(); tracing::debug!("append_entries to id={} {}", self.target, rpc); self.owner.count_rpc(RPCTypes::AppendEntries); @@ -1182,3 +1184,72 @@ impl From> for ValueTest { fn timeout() -> Option { Some(Duration::from_millis(5_000)) } + +/// A trait for extending the functionality of a Raft vote. +/// +/// This is used for testing. +pub(crate) trait TestingVoteExt +where + C: RaftTypeConfig, + Self: RaftVote, +{ + /// Creates a new vote for a node in a specific term, with uncommitted status. + fn from_term_node_id(term: C::Term, node_id: C::NodeId) -> Self { + let leader_id = C::LeaderId::new(term, node_id); + Self::from_leader_id(leader_id, false) + } + + /// Gets the node ID of the leader this vote is for, if present. + fn to_leader_node_id(&self) -> Option { + self.leader_node_id().cloned() + } + + /// Gets a reference to the node ID of the leader this vote is for, if present. + fn leader_node_id(&self) -> Option<&C::NodeId> { + self.leader_id().and_then(|x| x.node_id()) + } + + // /// Gets the leader ID this vote is associated with. + // fn to_leader_id(&self) -> C::LeaderId { + // self.leader_id().clone() + // } + // + // /// Creates a reference view of this vote. + // /// + // /// Returns a lightweight `RefVote` that borrows the data from this vote. + // fn as_ref_vote(&self) -> RefVote<'_, C> { + // RefVote::new(self.leader_id(), self.is_committed()) + // } + // + // /// Create a [`CommittedVote`] with the same leader id. + // fn to_committed(&self) -> CommittedVote { + // CommittedVote::new(self.to_leader_id()) + // } + // + // /// Create a [`NonCommittedVote`] with the same leader id. + // fn to_non_committed(&self) -> NonCommittedVote { + // NonCommittedVote::new(self.to_leader_id()) + // } + // + // /// Convert this vote into a [`CommittedVote`] + // fn into_committed(self) -> CommittedVote { + // CommittedVote::new(self.to_leader_id()) + // } + // + // /// Convert this vote into a [`NonCommittedVote`] + // fn into_non_committed(self) -> NonCommittedVote { + // NonCommittedVote::new(self.to_leader_id()) + // } + // + // /// Checks if this vote is for the same leader as specified by the given committed leader ID. + // fn is_same_leader(&self, leader_id: &CommittedLeaderIdOf) -> bool { + // self.leader_id().to_committed() == *leader_id + // } +} + +impl TestingVoteExt for T +where + C: RaftTypeConfig, + T: RaftVote, +{ +} From 6c0169553e293d73bd37740193645cd9fd890336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=82=8E=E6=B3=BC?= Date: Wed, 8 Jan 2025 12:03:32 +0800 Subject: [PATCH 2/4] CI: install cargo-audit manually --- .github/workflows/ci.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ba8f72c30..c142ea0e6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -369,10 +369,13 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + + - uses: actions-rs/toolchain@v1.0.6 with: components: rustfmt, clippy + - name: Format uses: actions-rs/cargo@v1 with: @@ -396,6 +399,10 @@ jobs: RUSTDOCFLAGS: "-D warnings" + - shell: bash + run: cargo install cargo-audit + + - name: Audit dependencies shell: bash # if: "!contains(github.event.head_commit.message, 'skip audit')" From 5fb5473af96fbc2f0a6929bb180066399cb75443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=82=8E=E6=B3=BC?= Date: Wed, 8 Jan 2025 12:26:56 +0800 Subject: [PATCH 3/4] CI: update rust-toolchain to 2025-01-01 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index f9f643455..09a243d78 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2024-07-02 \ No newline at end of file +nightly-2025-01-01 From 9d1be303bbd71399a048927845055c624698e312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=82=8E=E6=B3=BC?= Date: Wed, 8 Jan 2025 12:39:30 +0800 Subject: [PATCH 4/4] Chore: fix lint warnings --- openraft/src/core/raft_core.rs | 7 ++----- openraft/src/display_ext/display_btreemap_opt_value.rs | 2 +- openraft/src/display_ext/display_instant.rs | 2 +- openraft/src/display_ext/display_option.rs | 2 +- openraft/src/display_ext/display_result.rs | 2 +- openraft/src/display_ext/display_slice.rs | 2 +- openraft/src/docs/faq/mod.rs | 1 - openraft/src/docs/mod.rs | 3 --- openraft/src/engine/handler/following_handler/mod.rs | 2 +- openraft/src/engine/handler/leader_handler/mod.rs | 2 +- openraft/src/engine/handler/log_handler/mod.rs | 2 +- openraft/src/engine/handler/replication_handler/mod.rs | 2 +- openraft/src/engine/handler/server_state_handler/mod.rs | 2 +- openraft/src/engine/handler/snapshot_handler/mod.rs | 2 +- openraft/src/engine/handler/vote_handler/mod.rs | 2 +- openraft/src/error/invalid_sm.rs | 1 - openraft/src/metrics/metric_display.rs | 2 +- openraft/src/metrics/serde_instant.rs | 2 +- openraft/src/progress/mod.rs | 2 +- openraft/src/quorum/joint.rs | 2 +- openraft/src/raft_state/io_state/io_progress.rs | 6 +++--- openraft/src/raft_state/membership_state/change_handler.rs | 2 +- openraft/src/runtime/mod.rs | 2 +- openraft/src/summary.rs | 4 ++-- openraft/src/utime.rs | 2 +- openraft/src/vote/committed.rs | 2 +- openraft/src/vote/non_committed.rs | 2 +- openraft/src/vote/raft_vote.rs | 6 +++--- openraft/src/vote/ref_vote.rs | 2 +- 29 files changed, 32 insertions(+), 40 deletions(-) diff --git a/openraft/src/core/raft_core.rs b/openraft/src/core/raft_core.rs index 230815e85..08a6001b9 100644 --- a/openraft/src/core/raft_core.rs +++ b/openraft/src/core/raft_core.rs @@ -752,10 +752,7 @@ where } pub(crate) fn get_leader_node(&self, leader_id: Option) -> Option { - let leader_id = match leader_id { - None => return None, - Some(x) => x, - }; + let leader_id = leader_id?; self.engine.state.membership_state.effective().get_node(&leader_id).cloned() } @@ -1679,7 +1676,7 @@ where N: RaftNetworkFactory, LS: RaftLogStorage, { - async fn run_command<'e>(&mut self, cmd: Command) -> Result>, StorageError> { + async fn run_command(&mut self, cmd: Command) -> Result>, StorageError> { // tracing::debug!("RAFT_event id={:<2} trycmd: {}", self.id, cmd); let condition = cmd.condition(); diff --git a/openraft/src/display_ext/display_btreemap_opt_value.rs b/openraft/src/display_ext/display_btreemap_opt_value.rs index e7e353e5f..b56a1c14c 100644 --- a/openraft/src/display_ext/display_btreemap_opt_value.rs +++ b/openraft/src/display_ext/display_btreemap_opt_value.rs @@ -11,7 +11,7 @@ use super::DisplayOption; /// For how to format the `opt_value`, see [`DisplayOption`]. pub(crate) struct DisplayBTreeMapOptValue<'a, K: fmt::Display, V: fmt::Display>(pub &'a BTreeMap>); -impl<'a, K: fmt::Display, V: fmt::Display> fmt::Display for DisplayBTreeMapOptValue<'a, K, V> { +impl fmt::Display for DisplayBTreeMapOptValue<'_, K, V> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let len = self.0.len(); for (idx, (key, value)) in self.0.iter().enumerate() { diff --git a/openraft/src/display_ext/display_instant.rs b/openraft/src/display_ext/display_instant.rs index 7e8a7fff9..67515896d 100644 --- a/openraft/src/display_ext/display_instant.rs +++ b/openraft/src/display_ext/display_instant.rs @@ -36,7 +36,7 @@ impl<'a, T, const SIMPLE: bool, const LOCAL: bool> DisplayInstant<'a, T, SIMPLE, } } -impl<'a, T, const SIMPLE: bool, const LOCAL: bool> fmt::Display for DisplayInstant<'a, T, SIMPLE, LOCAL> +impl fmt::Display for DisplayInstant<'_, T, SIMPLE, LOCAL> where T: Instant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/openraft/src/display_ext/display_option.rs b/openraft/src/display_ext/display_option.rs index 76a821305..bf84e692e 100644 --- a/openraft/src/display_ext/display_option.rs +++ b/openraft/src/display_ext/display_option.rs @@ -6,7 +6,7 @@ use std::fmt; /// implementation for T. pub(crate) struct DisplayOption<'a, T: fmt::Display>(pub &'a Option); -impl<'a, T: fmt::Display> fmt::Display for DisplayOption<'a, T> { +impl fmt::Display for DisplayOption<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0 { None => { diff --git a/openraft/src/display_ext/display_result.rs b/openraft/src/display_ext/display_result.rs index ed769c383..7db7690f1 100644 --- a/openraft/src/display_ext/display_result.rs +++ b/openraft/src/display_ext/display_result.rs @@ -5,7 +5,7 @@ use std::fmt; /// It outputs `"Ok(...)"` or `"Err(...)"`. pub(crate) struct DisplayResult<'a, T: fmt::Display, E: fmt::Display>(pub &'a Result); -impl<'a, T, E> fmt::Display for DisplayResult<'a, T, E> +impl fmt::Display for DisplayResult<'_, T, E> where T: fmt::Display, E: fmt::Display, diff --git a/openraft/src/display_ext/display_slice.rs b/openraft/src/display_ext/display_slice.rs index cfb8538e4..74d8b33f3 100644 --- a/openraft/src/display_ext/display_slice.rs +++ b/openraft/src/display_ext/display_slice.rs @@ -6,7 +6,7 @@ use std::fmt; /// - `DisplaySlice(&[1,2,3,4,5,6])` outputs: `"[1,2,3,4,...,6]"`. pub(crate) struct DisplaySlice<'a, T: fmt::Display, const MAX: usize = 5>(pub &'a [T]); -impl<'a, T: fmt::Display, const MAX: usize> fmt::Display for DisplaySlice<'a, T, MAX> { +impl fmt::Display for DisplaySlice<'_, T, MAX> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let slice = self.0; let len = slice.len(); diff --git a/openraft/src/docs/faq/mod.rs b/openraft/src/docs/faq/mod.rs index 7eba4e344..ea5f94237 100644 --- a/openraft/src/docs/faq/mod.rs +++ b/openraft/src/docs/faq/mod.rs @@ -1,4 +1,3 @@ //! # FAQ #![doc = include_str!("faq-toc.md")] - #![doc = include_str!("faq.md")] diff --git a/openraft/src/docs/mod.rs b/openraft/src/docs/mod.rs index 33ff577fe..381762298 100644 --- a/openraft/src/docs/mod.rs +++ b/openraft/src/docs/mod.rs @@ -1,9 +1,6 @@ #![allow(rustdoc::redundant_explicit_links)] #![doc = include_str!("docs.md")] -#[rustfmt::skip] - - pub mod faq; pub mod getting_started; diff --git a/openraft/src/engine/handler/following_handler/mod.rs b/openraft/src/engine/handler/following_handler/mod.rs index 9fba6b744..1a0a9d4bc 100644 --- a/openraft/src/engine/handler/following_handler/mod.rs +++ b/openraft/src/engine/handler/following_handler/mod.rs @@ -52,7 +52,7 @@ where C: RaftTypeConfig pub(crate) output: &'x mut EngineOutput, } -impl<'x, C> FollowingHandler<'x, C> +impl FollowingHandler<'_, C> where C: RaftTypeConfig { /// Append entries to follower/learner. diff --git a/openraft/src/engine/handler/leader_handler/mod.rs b/openraft/src/engine/handler/leader_handler/mod.rs index 2fca36c73..85563ebe4 100644 --- a/openraft/src/engine/handler/leader_handler/mod.rs +++ b/openraft/src/engine/handler/leader_handler/mod.rs @@ -37,7 +37,7 @@ where C: RaftTypeConfig pub(crate) output: &'x mut EngineOutput, } -impl<'x, C> LeaderHandler<'x, C> +impl LeaderHandler<'_, C> where C: RaftTypeConfig { /// Append new log entries by a leader. diff --git a/openraft/src/engine/handler/log_handler/mod.rs b/openraft/src/engine/handler/log_handler/mod.rs index f72101a64..bb3d67ec2 100644 --- a/openraft/src/engine/handler/log_handler/mod.rs +++ b/openraft/src/engine/handler/log_handler/mod.rs @@ -22,7 +22,7 @@ where C: RaftTypeConfig pub(crate) output: &'x mut EngineOutput, } -impl<'x, C> LogHandler<'x, C> +impl LogHandler<'_, C> where C: RaftTypeConfig { /// Purge log entries upto `RaftState.purge_upto()`, inclusive. diff --git a/openraft/src/engine/handler/replication_handler/mod.rs b/openraft/src/engine/handler/replication_handler/mod.rs index 75b9012d5..6cce15526 100644 --- a/openraft/src/engine/handler/replication_handler/mod.rs +++ b/openraft/src/engine/handler/replication_handler/mod.rs @@ -50,7 +50,7 @@ where C: RaftTypeConfig pub(crate) output: &'x mut EngineOutput, } -impl<'x, C> ReplicationHandler<'x, C> +impl ReplicationHandler<'_, C> where C: RaftTypeConfig { /// Append a new membership and update related state such as replication streams. diff --git a/openraft/src/engine/handler/server_state_handler/mod.rs b/openraft/src/engine/handler/server_state_handler/mod.rs index 9b0603336..923e3596f 100644 --- a/openraft/src/engine/handler/server_state_handler/mod.rs +++ b/openraft/src/engine/handler/server_state_handler/mod.rs @@ -14,7 +14,7 @@ where C: RaftTypeConfig pub(crate) state: &'st mut RaftState, } -impl<'st, C> ServerStateHandler<'st, C> +impl ServerStateHandler<'_, C> where C: RaftTypeConfig { /// Re-calculate the server-state, if it changed, update the `server_state` field and dispatch diff --git a/openraft/src/engine/handler/snapshot_handler/mod.rs b/openraft/src/engine/handler/snapshot_handler/mod.rs index 78700a58d..9aecfbf59 100644 --- a/openraft/src/engine/handler/snapshot_handler/mod.rs +++ b/openraft/src/engine/handler/snapshot_handler/mod.rs @@ -22,7 +22,7 @@ where C: RaftTypeConfig pub(crate) output: &'out mut EngineOutput, } -impl<'st, 'out, C> SnapshotHandler<'st, 'out, C> +impl SnapshotHandler<'_, '_, C> where C: RaftTypeConfig { /// Trigger building snapshot if there is no pending building job. diff --git a/openraft/src/engine/handler/vote_handler/mod.rs b/openraft/src/engine/handler/vote_handler/mod.rs index d54bc1dd1..7dd55b954 100644 --- a/openraft/src/engine/handler/vote_handler/mod.rs +++ b/openraft/src/engine/handler/vote_handler/mod.rs @@ -49,7 +49,7 @@ where C: RaftTypeConfig pub(crate) candidate: &'st mut CandidateState, } -impl<'st, C> VoteHandler<'st, C> +impl VoteHandler<'_, C> where C: RaftTypeConfig { /// Validate and accept the input `vote` and send result via `tx`. diff --git a/openraft/src/error/invalid_sm.rs b/openraft/src/error/invalid_sm.rs index 9beca1682..60704476e 100644 --- a/openraft/src/error/invalid_sm.rs +++ b/openraft/src/error/invalid_sm.rs @@ -4,7 +4,6 @@ It may have used a different type \ of state machine from the one in RaftCore (`{actual_type}`)" )] - pub struct InvalidStateMachineType { pub actual_type: &'static str, } diff --git a/openraft/src/metrics/metric_display.rs b/openraft/src/metrics/metric_display.rs index f27585474..8b4bec132 100644 --- a/openraft/src/metrics/metric_display.rs +++ b/openraft/src/metrics/metric_display.rs @@ -12,7 +12,7 @@ where C: RaftTypeConfig pub(crate) metric: &'a Metric, } -impl<'a, C> fmt::Display for MetricDisplay<'a, C> +impl fmt::Display for MetricDisplay<'_, C> where C: RaftTypeConfig { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { diff --git a/openraft/src/metrics/serde_instant.rs b/openraft/src/metrics/serde_instant.rs index 117e0a629..e87a61281 100644 --- a/openraft/src/metrics/serde_instant.rs +++ b/openraft/src/metrics/serde_instant.rs @@ -112,7 +112,7 @@ mod serde_impl { where D: Deserializer<'de> { struct InstantVisitor(PhantomData); - impl<'de, II: Instant> Visitor<'de> for InstantVisitor { + impl Visitor<'_> for InstantVisitor { type Value = SerdeInstant; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { diff --git a/openraft/src/progress/mod.rs b/openraft/src/progress/mod.rs index 693b3bcfc..0cde76d91 100644 --- a/openraft/src/progress/mod.rs +++ b/openraft/src/progress/mod.rs @@ -176,7 +176,7 @@ where f: Fmt, } -impl<'a, ID, V, P, QS, Fmt> Display for DisplayVecProgress<'a, ID, V, P, QS, Fmt> +impl Display for DisplayVecProgress<'_, ID, V, P, QS, Fmt> where ID: PartialEq + 'static, V: Borrow

, diff --git a/openraft/src/quorum/joint.rs b/openraft/src/quorum/joint.rs index 9488f5efd..c2b4778f3 100644 --- a/openraft/src/quorum/joint.rs +++ b/openraft/src/quorum/joint.rs @@ -46,7 +46,7 @@ where } /// Implement QuorumSet for `Joint<.., &[QS]>` -impl<'d, ID, QS> QuorumSet for Joint +impl QuorumSet for Joint where ID: PartialOrd + Ord + 'static, QS: QuorumSet, diff --git a/openraft/src/raft_state/io_state/io_progress.rs b/openraft/src/raft_state/io_state/io_progress.rs index 325c97384..a4e2d7e8f 100644 --- a/openraft/src/raft_state/io_state/io_progress.rs +++ b/openraft/src/raft_state/io_state/io_progress.rs @@ -80,7 +80,7 @@ where /// Update the `accept` cursor of the I/O progress. pub(crate) fn accept(&mut self, new_accepted: T) { debug_assert!( - self.accepted.as_ref().map_or(true, |accepted| accepted <= &new_accepted), + self.accepted.as_ref().is_none_or(|accepted| accepted <= &new_accepted), "expect accepted:{} < new_accepted:{}", self.accepted.display(), new_accepted, @@ -92,7 +92,7 @@ where /// Update the `submit` cursor of the I/O progress. pub(crate) fn submit(&mut self, new_submitted: T) { debug_assert!( - self.submitted.as_ref().map_or(true, |submitted| submitted <= &new_submitted), + self.submitted.as_ref().is_none_or(|submitted| submitted <= &new_submitted), "expect submitted:{} < new_submitted:{}", self.submitted.display(), new_submitted, @@ -104,7 +104,7 @@ where /// Update the `flush` cursor of the I/O progress. pub(crate) fn flush(&mut self, new_flushed: T) { debug_assert!( - self.flushed.as_ref().map_or(true, |flushed| flushed <= &new_flushed), + self.flushed.as_ref().is_none_or(|flushed| flushed <= &new_flushed), "expect flushed:{} < new_flushed:{}", self.flushed.display(), new_flushed, diff --git a/openraft/src/raft_state/membership_state/change_handler.rs b/openraft/src/raft_state/membership_state/change_handler.rs index fdece9fb4..299dcc677 100644 --- a/openraft/src/raft_state/membership_state/change_handler.rs +++ b/openraft/src/raft_state/membership_state/change_handler.rs @@ -14,7 +14,7 @@ where C: RaftTypeConfig pub(crate) state: &'m MembershipState, } -impl<'m, C> ChangeHandler<'m, C> +impl ChangeHandler<'_, C> where C: RaftTypeConfig { /// Builds a new membership configuration by applying changes to the current configuration. diff --git a/openraft/src/runtime/mod.rs b/openraft/src/runtime/mod.rs index 1f8e38ac1..564a78b65 100644 --- a/openraft/src/runtime/mod.rs +++ b/openraft/src/runtime/mod.rs @@ -62,5 +62,5 @@ pub(crate) trait RaftRuntime { /// Run a command produced by the engine. /// /// If a command can not be run, i.e., waiting for some event, it will be returned - async fn run_command<'e>(&mut self, cmd: Command) -> Result>, StorageError>; + async fn run_command(&mut self, cmd: Command) -> Result>, StorageError>; } diff --git a/openraft/src/summary.rs b/openraft/src/summary.rs index 3f624e363..6263d1d85 100644 --- a/openraft/src/summary.rs +++ b/openraft/src/summary.rs @@ -39,8 +39,8 @@ where T: fmt::Display } } -impl<'a, T> MessageSummary for &[T] -where T: MessageSummary + 'a +impl MessageSummary for &[T] +where T: MessageSummary { fn summary(&self) -> String { if self.is_empty() { diff --git a/openraft/src/utime.rs b/openraft/src/utime.rs index 36e2d8196..ab12a8da7 100644 --- a/openraft/src/utime.rs +++ b/openraft/src/utime.rs @@ -94,7 +94,7 @@ impl Leased { leased: &'a Leased, } - impl<'a, T, I> fmt::Display for DisplayLeaseInfo<'a, T, I> + impl fmt::Display for DisplayLeaseInfo<'_, T, I> where I: Instant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/openraft/src/vote/committed.rs b/openraft/src/vote/committed.rs index 9bd07ac7e..02f374258 100644 --- a/openraft/src/vote/committed.rs +++ b/openraft/src/vote/committed.rs @@ -65,7 +65,7 @@ impl RaftVote for CommittedVote where C: RaftTypeConfig { fn from_leader_id(_leader_id: C::LeaderId, _committed: bool) -> Self { - unimplemented!() + unreachable!("CommittedVote should only be built from a Vote") } fn leader_id(&self) -> Option<&LeaderIdOf> { diff --git a/openraft/src/vote/non_committed.rs b/openraft/src/vote/non_committed.rs index 4003d12d1..a9212b2b8 100644 --- a/openraft/src/vote/non_committed.rs +++ b/openraft/src/vote/non_committed.rs @@ -35,7 +35,7 @@ impl RaftVote for NonCommittedVote where C: RaftTypeConfig { fn from_leader_id(_leader_id: C::LeaderId, _committed: bool) -> Self { - unimplemented!() + unreachable!("NonCommittedVote should only be built from a Vote") } fn leader_id(&self) -> Option<&LeaderIdOf> { diff --git a/openraft/src/vote/raft_vote.rs b/openraft/src/vote/raft_vote.rs index f88cf45ec..a5205bf12 100644 --- a/openraft/src/vote/raft_vote.rs +++ b/openraft/src/vote/raft_vote.rs @@ -19,13 +19,13 @@ where C: RaftTypeConfig, Self: OptionalFeatures + Eq + Clone + Debug + Display + Default + 'static, { - /// Create a new vote for the specified leader with optional quorum commitment + /// Create a new vote for the specified leader with optional quorum commitment. fn from_leader_id(leader_id: C::LeaderId, committed: bool) -> Self; - /// Get a reference to this vote's LeaderId([`RaftLeaderId`] implementation) + /// Get a reference to this vote's LeaderId([`RaftLeaderId`] implementation). fn leader_id(&self) -> Option<&C::LeaderId>; - /// Whether this vote has been committed by a quorum + /// Whether this vote has been committed by a quorum. fn is_committed(&self) -> bool; } diff --git a/openraft/src/vote/ref_vote.rs b/openraft/src/vote/ref_vote.rs index ef454ba16..23a72c12f 100644 --- a/openraft/src/vote/ref_vote.rs +++ b/openraft/src/vote/ref_vote.rs @@ -54,7 +54,7 @@ where C: RaftTypeConfig } } -impl<'a, C> std::fmt::Display for RefVote<'a, C> +impl std::fmt::Display for RefVote<'_, C> where C: RaftTypeConfig { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {