blob: 5975abe8ac3523fd79006483fd95fe40baf2e6f9 [file] [log] [blame]
// 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 std::prelude::v1::*;
pub use super::read_only::{ReadOnlyOption, ReadState};
use super::{
errors::{Error, Result},
INVALID_ID,
};
/// 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<u64>,
/// 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<u64>,
/// 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(())
}
}