1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
use std::fmt;
use crate::display_ext::DisplayOptionExt;
use crate::display_ext::DisplaySlice;
use crate::LogId;
use crate::MessageSummary;
use crate::NodeId;
use crate::RaftTypeConfig;
use crate::Vote;
/// An RPC sent by a cluster leader to replicate log entries (§5.3), and as a heartbeat (§5.2).
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound = ""))]
pub struct AppendEntriesRequest<C: RaftTypeConfig> {
pub vote: Vote<C::NodeId>,
pub prev_log_id: Option<LogId<C::NodeId>>,
/// The new log entries to store.
///
/// This may be empty when the leader is sending heartbeats. Entries
/// are batched for efficiency.
pub entries: Vec<C::Entry>,
/// The leader's committed log id.
pub leader_commit: Option<LogId<C::NodeId>>,
}
impl<C: RaftTypeConfig> fmt::Debug for AppendEntriesRequest<C>
where C::D: fmt::Debug
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AppendEntriesRequest")
.field("vote", &self.vote)
.field("prev_log_id", &self.prev_log_id)
.field("entries", &self.entries)
.field("leader_commit", &self.leader_commit)
.finish()
}
}
impl<C: RaftTypeConfig> MessageSummary<AppendEntriesRequest<C>> for AppendEntriesRequest<C> {
fn summary(&self) -> String {
format!(
"vote={}, prev_log_id={}, leader_commit={}, entries={}",
self.vote,
self.prev_log_id.summary(),
self.leader_commit.summary(),
DisplaySlice::<_>(self.entries.as_slice())
)
}
}
/// The response to an `AppendEntriesRequest`.
///
/// [`RaftNetwork::append_entries`] returns this type only when received an RPC reply.
/// Otherwise it should return [`RPCError`].
///
/// [`RPCError`]: crate::error::RPCError
/// [`RaftNetwork::append_entries`]: crate::network::RaftNetwork::append_entries
#[derive(Debug)]
#[derive(PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize), serde(bound = ""))]
pub enum AppendEntriesResponse<NID: NodeId> {
/// Successfully replicated all log entries to the target node.
Success,
/// Successfully sent the first portion of log entries.
///
/// [`RaftNetwork::append_entries`] can return a partial success.
/// For example, it tries to send log entries `[1-2..3-10]`, the application is allowed to send
/// just `[1-2..1-3]` and return `PartialSuccess(1-3)`,
///
/// ### Caution
///
/// The returned matching log id must be **greater than or equal to** the first log
/// id([`AppendEntriesRequest::prev_log_id`]) of the entries to send. If no RPC reply is
/// received, [`RaftNetwork::append_entries`] must return an [`RPCError`] to inform
/// Openraft that the first log id([`AppendEntriesRequest::prev_log_id`]) may not match on
/// the remote target node.
///
/// [`RPCError`]: crate::error::RPCError
/// [`RaftNetwork::append_entries`]: crate::network::RaftNetwork::append_entries
PartialSuccess(Option<LogId<NID>>),
/// The first log id([`AppendEntriesRequest::prev_log_id`]) of the entries to send does not
/// match on the remote target node.
Conflict,
/// Seen a vote `v` that does not hold `mine_vote >= v`.
/// And a leader's vote(committed vote) must be total order with other vote.
/// Therefore it has to be a higher vote: `mine_vote < v`
HigherVote(Vote<NID>),
}
impl<NID: NodeId> AppendEntriesResponse<NID> {
pub fn is_success(&self) -> bool {
matches!(*self, AppendEntriesResponse::Success)
}
pub fn is_conflict(&self) -> bool {
matches!(*self, AppendEntriesResponse::Conflict)
}
}
impl<NID: NodeId> fmt::Display for AppendEntriesResponse<NID> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AppendEntriesResponse::Success => write!(f, "Success"),
AppendEntriesResponse::PartialSuccess(m) => {
write!(f, "PartialSuccess({})", m.display())
}
AppendEntriesResponse::HigherVote(vote) => write!(f, "Higher vote, {}", vote),
AppendEntriesResponse::Conflict => write!(f, "Conflict"),
}
}
}