From 935840b7b0ad14a1497779915ec6986558355325 Mon Sep 17 00:00:00 2001 From: "A. Hobden" Date: Wed, 1 Aug 2018 08:57:29 -0700 Subject: [PATCH] config: Extract to own file. (#91) --- src/config.rs | 206 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 7 +- src/raft.rs | 178 +---------------------------------------- src/raw_node.rs | 3 +- 4 files changed, 214 insertions(+), 180 deletions(-) create mode 100644 src/config.rs diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 000000000..c94a93768 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,206 @@ +// Copyright 2016 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +// Copyright 2015 The etcd Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::{INVALID_ID, errors::{Error, Result}}; +pub use super::read_only::{ReadOnlyOption, ReadState}; + +/// Config contains the parameters to start a raft. +pub struct Config { + /// The identity of the local raft. It cannot be 0, and must be unique in the group. + pub id: u64, + + /// The IDs of all nodes (including self) in + /// the raft cluster. It should only be set when starting a new + /// raft cluster. + /// Restarting raft from previous configuration will panic if + /// peers is set. + /// peer is private and only used for testing right now. + pub peers: Vec, + + /// The IDs of all learner nodes (maybe include self if + /// the local node is a learner) in the raft cluster. + /// learners only receives entries from the leader node. It does not vote + /// or promote itself. + pub learners: Vec, + + /// The number of node.tick invocations that must pass between + /// elections. That is, if a follower does not receive any message from the + /// leader of current term before ElectionTick has elapsed, it will become + /// candidate and start an election. election_tick must be greater than + /// HeartbeatTick. We suggest election_tick = 10 * HeartbeatTick to avoid + /// unnecessary leader switching + pub election_tick: usize, + + /// HeartbeatTick is the number of node.tick invocations that must pass between + /// heartbeats. That is, a leader sends heartbeat messages to maintain its + /// leadership every heartbeat ticks. + pub heartbeat_tick: usize, + + /// Applied is the last applied index. It should only be set when restarting + /// raft. raft will not return entries to the application smaller or equal to Applied. + /// If Applied is unset when restarting, raft might return previous applied entries. + /// This is a very application dependent configuration. + pub applied: u64, + + /// Limit the max size of each append message. Smaller value lowers + /// the raft recovery cost(initial probing and message lost during normal operation). + /// On the other side, it might affect the throughput during normal replication. + /// Note: math.MaxUusize64 for unlimited, 0 for at most one entry per message. + pub max_size_per_msg: u64, + + /// Limit the max number of in-flight append messages during optimistic + /// replication phase. The application transportation layer usually has its own sending + /// buffer over TCP/UDP. Set to avoid overflowing that sending buffer. + /// TODO: feedback to application to limit the proposal rate? + pub max_inflight_msgs: usize, + + /// Specify if the leader should check quorum activity. Leader steps down when + /// quorum is not active for an electionTimeout. + pub check_quorum: bool, + + /// Enables the Pre-Vote algorithm described in raft thesis section + /// 9.6. This prevents disruption when a node that has been partitioned away + /// rejoins the cluster. + pub pre_vote: bool, + + /// The range of election timeout. In some cases, we hope some nodes has less possibility + /// to become leader. This configuration ensures that the randomized election_timeout + /// will always be suit in [min_election_tick, max_election_tick). + /// If it is 0, then election_tick will be chosen. + pub min_election_tick: usize, + + /// If it is 0, then 2 * election_tick will be chosen. + pub max_election_tick: usize, + + /// Choose the linearizability mode or the lease mode to read data. If you don’t care about the read consistency and want a higher read performance, you can use the lease mode. + pub read_only_option: ReadOnlyOption, + + /// Don't broadcast an empty raft entry to notify follower to commit an entry. + /// This may make follower wait a longer time to apply an entry. This configuration + /// May affect proposal forwarding and follower read. + pub skip_bcast_commit: bool, + + /// A human-friendly tag used for logging. + pub tag: String, +} + +impl Default for Config { + fn default() -> Self { + const HEARTBEAT_TICK: usize = 2; + Self { + id: 0, + peers: vec![], + learners: vec![], + election_tick: HEARTBEAT_TICK * 10, + heartbeat_tick: HEARTBEAT_TICK, + applied: 0, + max_size_per_msg: 0, + max_inflight_msgs: 256, + check_quorum: false, + pre_vote: false, + min_election_tick: 0, + max_election_tick: 0, + read_only_option: ReadOnlyOption::Safe, + skip_bcast_commit: false, + tag: "".into(), + } + } +} + +impl Config { + /// Creates a new config. + pub fn new(id: u64) -> Self { + Self { + id, + tag: format!("{}", id), + ..Self::default() + } + } + + /// The minimum number of ticks before an election. + #[inline] + pub fn min_election_tick(&self) -> usize { + if self.min_election_tick == 0 { + self.election_tick + } else { + self.min_election_tick + } + } + + /// The maximum number of ticks before an election. + #[inline] + pub fn max_election_tick(&self) -> usize { + if self.max_election_tick == 0 { + 2 * self.election_tick + } else { + self.max_election_tick + } + } + + /// Runs validations against the config. + pub fn validate(&self) -> Result<()> { + if self.id == INVALID_ID { + return Err(Error::ConfigInvalid("invalid node id".to_owned())); + } + + if self.heartbeat_tick == 0 { + return Err(Error::ConfigInvalid( + "heartbeat tick must greater than 0".to_owned(), + )); + } + + if self.election_tick <= self.heartbeat_tick { + return Err(Error::ConfigInvalid( + "election tick must be greater than heartbeat tick".to_owned(), + )); + } + + let min_timeout = self.min_election_tick(); + let max_timeout = self.max_election_tick(); + if min_timeout < self.election_tick { + return Err(Error::ConfigInvalid(format!( + "min election tick {} must not be less than election_tick {}", + min_timeout, self.election_tick + ))); + } + + if min_timeout >= max_timeout { + return Err(Error::ConfigInvalid(format!( + "min election tick {} should be less than max election tick {}", + min_timeout, max_timeout + ))); + } + + if self.max_inflight_msgs == 0 { + return Err(Error::ConfigInvalid( + "max inflight messages must be greater than 0".to_owned(), + )); + } + + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index ef189d497..8b8b079db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -229,6 +229,7 @@ extern crate rand; /// documented by field. pub mod eraftpb; mod errors; +mod config; mod log_unstable; mod progress; mod raft; @@ -243,8 +244,9 @@ pub use self::errors::{Error, Result, StorageError}; pub use self::log_unstable::Unstable; pub use self::progress::{Inflights, Progress, ProgressSet, ProgressState}; pub use self::raft::{ - quorum, vote_resp_msg_type, Config, Raft, SoftState, StateRole, INVALID_ID, INVALID_INDEX, + quorum, vote_resp_msg_type, Raft, SoftState, StateRole, INVALID_ID, INVALID_INDEX, }; +pub use self::config::Config; pub use self::raft_log::{RaftLog, NO_LIMIT}; pub use self::raw_node::{is_empty_snap, Peer, RawNode, Ready, SnapshotStatus}; pub use self::read_only::{ReadOnlyOption, ReadState}; @@ -269,7 +271,8 @@ pub mod prelude { Snapshot, SnapshotMetadata, }; - pub use raft::{Config, Raft}; + pub use raft::Raft; + pub use config::Config; pub use storage::{RaftState, Storage}; diff --git a/src/raft.rs b/src/raft.rs index 2464400f4..8b86d9cab 100644 --- a/src/raft.rs +++ b/src/raft.rs @@ -33,6 +33,7 @@ use protobuf::RepeatedField; use rand::{self, Rng}; use super::errors::{Error, Result, StorageError}; +use super::Config; use super::progress::{Inflights, Progress, ProgressSet, ProgressState}; use super::raft_log::{self, RaftLog}; use super::read_only::{ReadOnly, ReadOnlyOption, ReadState}; @@ -71,183 +72,6 @@ pub const INVALID_ID: u64 = 0; /// A constant represents invalid index of raft log. pub const INVALID_INDEX: u64 = 0; -/// Config contains the parameters to start a raft. -pub struct Config { - /// The identity of the local raft. It cannot be 0, and must be unique in the group. - pub id: u64, - - /// The IDs of all nodes (including self) in - /// the raft cluster. It should only be set when starting a new - /// raft cluster. - /// Restarting raft from previous configuration will panic if - /// peers is set. - /// peer is private and only used for testing right now. - pub peers: Vec, - - /// The IDs of all learner nodes (maybe include self if - /// the local node is a learner) in the raft cluster. - /// learners only receives entries from the leader node. It does not vote - /// or promote itself. - pub learners: Vec, - - /// The number of node.tick invocations that must pass between - /// elections. That is, if a follower does not receive any message from the - /// leader of current term before ElectionTick has elapsed, it will become - /// candidate and start an election. election_tick must be greater than - /// HeartbeatTick. We suggest election_tick = 10 * HeartbeatTick to avoid - /// unnecessary leader switching - pub election_tick: usize, - - /// HeartbeatTick is the number of node.tick invocations that must pass between - /// heartbeats. That is, a leader sends heartbeat messages to maintain its - /// leadership every heartbeat ticks. - pub heartbeat_tick: usize, - - /// Applied is the last applied index. It should only be set when restarting - /// raft. raft will not return entries to the application smaller or equal to Applied. - /// If Applied is unset when restarting, raft might return previous applied entries. - /// This is a very application dependent configuration. - pub applied: u64, - - /// Limit the max size of each append message. Smaller value lowers - /// the raft recovery cost(initial probing and message lost during normal operation). - /// On the other side, it might affect the throughput during normal replication. - /// Note: math.MaxUusize64 for unlimited, 0 for at most one entry per message. - pub max_size_per_msg: u64, - - /// Limit the max number of in-flight append messages during optimistic - /// replication phase. The application transportation layer usually has its own sending - /// buffer over TCP/UDP. Set to avoid overflowing that sending buffer. - /// TODO: feedback to application to limit the proposal rate? - pub max_inflight_msgs: usize, - - /// Specify if the leader should check quorum activity. Leader steps down when - /// quorum is not active for an electionTimeout. - pub check_quorum: bool, - - /// Enables the Pre-Vote algorithm described in raft thesis section - /// 9.6. This prevents disruption when a node that has been partitioned away - /// rejoins the cluster. - pub pre_vote: bool, - - /// The range of election timeout. In some cases, we hope some nodes has less possibility - /// to become leader. This configuration ensures that the randomized election_timeout - /// will always be suit in [min_election_tick, max_election_tick). - /// If it is 0, then election_tick will be chosen. - pub min_election_tick: usize, - - /// If it is 0, then 2 * election_tick will be chosen. - pub max_election_tick: usize, - - /// Choose the linearizability mode or the lease mode to read data. If you don’t care about the read consistency and want a higher read performance, you can use the lease mode. - pub read_only_option: ReadOnlyOption, - - /// Don't broadcast an empty raft entry to notify follower to commit an entry. - /// This may make follower wait a longer time to apply an entry. This configuration - /// May affect proposal forwarding and follower read. - pub skip_bcast_commit: bool, - - /// A human-friendly tag used for logging. - pub tag: String, -} - -impl Default for Config { - fn default() -> Self { - const HEARTBEAT_TICK: usize = 2; - Self { - id: 0, - peers: vec![], - learners: vec![], - election_tick: HEARTBEAT_TICK * 10, - heartbeat_tick: HEARTBEAT_TICK, - applied: 0, - max_size_per_msg: 0, - max_inflight_msgs: 256, - check_quorum: false, - pre_vote: false, - min_election_tick: 0, - max_election_tick: 0, - read_only_option: ReadOnlyOption::Safe, - skip_bcast_commit: false, - tag: "".into(), - } - } -} - -impl Config { - /// Creates a new config. - pub fn new(id: u64) -> Self { - Self { - id, - tag: format!("{}", id), - ..Self::default() - } - } - - /// The minimum number of ticks before an election. - #[inline] - pub fn min_election_tick(&self) -> usize { - if self.min_election_tick == 0 { - self.election_tick - } else { - self.min_election_tick - } - } - - /// The maximum number of ticks before an election. - #[inline] - pub fn max_election_tick(&self) -> usize { - if self.max_election_tick == 0 { - 2 * self.election_tick - } else { - self.max_election_tick - } - } - - /// Runs validations against the config. - pub fn validate(&self) -> Result<()> { - if self.id == INVALID_ID { - return Err(Error::ConfigInvalid("invalid node id".to_owned())); - } - - if self.heartbeat_tick == 0 { - return Err(Error::ConfigInvalid( - "heartbeat tick must greater than 0".to_owned(), - )); - } - - if self.election_tick <= self.heartbeat_tick { - return Err(Error::ConfigInvalid( - "election tick must be greater than heartbeat tick".to_owned(), - )); - } - - let min_timeout = self.min_election_tick(); - let max_timeout = self.max_election_tick(); - if min_timeout < self.election_tick { - return Err(Error::ConfigInvalid(format!( - "min election tick {} must not be less than election_tick {}", - min_timeout, self.election_tick - ))); - } - - if min_timeout >= max_timeout { - return Err(Error::ConfigInvalid(format!( - "min election tick {} should be less than max election tick {}", - min_timeout, max_timeout - ))); - } - - if self.max_inflight_msgs == 0 { - return Err(Error::ConfigInvalid( - "max inflight messages must be greater than 0".to_owned(), - )); - } - - Ok(()) - } -} - /// SoftState provides state that is useful for logging and debugging. /// The state is volatile and does not need to be persisted to the WAL. #[derive(Default, PartialEq, Debug)] diff --git a/src/raw_node.rs b/src/raw_node.rs index 011c8b791..449777950 100644 --- a/src/raw_node.rs +++ b/src/raw_node.rs @@ -39,7 +39,8 @@ use eraftpb::{ use protobuf::{self, RepeatedField}; use super::errors::{Error, Result}; -use super::raft::{Config, Raft, SoftState, INVALID_ID}; +use super::{Raft, SoftState, INVALID_ID}; +use super::config::Config; use super::read_only::ReadState; use super::Status; use super::Storage;