use std::sync::Arc;
use std::time::Duration;
use display_more::DisplayOptionExt;
use futures_util::Stream;
use openraft_macros::since;
use crate::OptionalSend;
use crate::RaftTypeConfig;
use crate::core::io_flush_tracking::FlushPoint;
use crate::core::raft_msg::RaftMsg;
use crate::core::raft_msg::external_command::ExternalCommand;
use crate::errors::Fatal;
#[cfg(doc)]
use crate::errors::into_raft_result::IntoRaftResult;
use crate::raft::AppendEntriesRequest;
use crate::raft::AppendEntriesResponse;
use crate::raft::SnapshotResponse;
use crate::raft::TransferLeaderError;
use crate::raft::TransferLeaderRequest;
use crate::raft::TransferLeaderResponse;
use crate::raft::VoteRequest;
use crate::raft::VoteResponse;
use crate::raft::raft_inner::RaftInner;
use crate::raft::stream_append;
use crate::raft::stream_append::StreamAppendResult;
use crate::type_config::TypeConfigExt;
use crate::type_config::alias::SnapshotDataOf;
use crate::type_config::alias::SnapshotOf;
use crate::type_config::alias::VoteOf;
use crate::vote::RaftVote;
use crate::vote::raft_vote::RaftVoteExt;
#[since(version = "0.10.0")]
pub(crate) struct ProtocolApi<C>
where C: RaftTypeConfig
{
inner: Arc<RaftInner<C>>,
}
impl<C> ProtocolApi<C>
where C: RaftTypeConfig
{
pub(in crate::raft) fn new(inner: Arc<RaftInner<C>>) -> Self {
Self { inner }
}
#[since(version = "0.10.0")]
#[tracing::instrument(level = "debug", skip(self, rpc))]
pub(crate) async fn vote(&self, rpc: VoteRequest<C>) -> Result<VoteResponse<C>, Fatal<C>> {
tracing::info!("Raft::vote(): rpc: {}", rpc);
let (tx, rx) = C::oneshot();
self.inner.call_core(RaftMsg::RequestVote { rpc, tx }, rx).await
}
#[tracing::instrument(level = "debug", skip(self, rpc))]
pub(crate) async fn pre_vote(&self, rpc: VoteRequest<C>) -> Result<VoteResponse<C>, Fatal<C>> {
tracing::info!("Raft::pre_vote(): rpc: {}", rpc);
let (tx, rx) = C::oneshot();
self.inner.call_core(RaftMsg::RequestPreVote { rpc, tx }, rx).await
}
#[since(version = "0.10.0")]
#[tracing::instrument(level = "debug", skip(self, rpc))]
pub(crate) async fn append_entries(
&self,
rpc: AppendEntriesRequest<C>,
) -> Result<AppendEntriesResponse<C>, Fatal<C>> {
tracing::debug!("Raft::append_entries: rpc: {}", rpc);
let (tx, rx) = C::oneshot();
let stream_result: StreamAppendResult<C> = self.inner.call_core(RaftMsg::AppendEntries { rpc, tx }, rx).await?;
Ok(AppendEntriesResponse::from(stream_result))
}
#[since(version = "0.10.0")]
pub(crate) fn stream_append<S>(
self,
stream: S,
) -> impl Stream<Item = Result<StreamAppendResult<C>, Fatal<C>>> + OptionalSend + 'static
where
S: Stream<Item = AppendEntriesRequest<C>> + OptionalSend + 'static,
{
stream_append::stream_append(self.inner, stream)
}
#[since(version = "0.10.0")]
#[tracing::instrument(level = "debug", skip_all)]
pub(crate) async fn get_snapshot(&self) -> Result<Option<SnapshotOf<C>>, Fatal<C>> {
tracing::debug!("Raft::get_snapshot()");
let (tx, rx) = C::oneshot();
let cmd = ExternalCommand::GetSnapshot { tx };
self.inner.call_core(RaftMsg::ExternalCommand { cmd }, rx).await
}
#[since(version = "0.10.0")]
#[tracing::instrument(level = "debug", skip_all)]
pub(crate) async fn begin_receiving_snapshot(&self) -> Result<SnapshotDataOf<C>, Fatal<C>> {
tracing::info!("Raft::begin_receiving_snapshot()");
let (tx, rx) = C::oneshot();
self.inner.call_core(RaftMsg::GetSnapshotReceiver { tx }, rx).await
}
#[since(version = "0.10.0")]
#[tracing::instrument(level = "debug", skip_all)]
pub(crate) async fn install_full_snapshot(
&self,
vote: VoteOf<C>,
snapshot: SnapshotOf<C>,
) -> Result<SnapshotResponse<C>, Fatal<C>> {
tracing::info!("Raft::install_full_snapshot()");
let (tx, rx) = C::oneshot();
self.inner.call_core(RaftMsg::InstallSnapshot { vote, snapshot, tx }, rx).await
}
#[since(version = "0.10.0")]
pub(crate) async fn handle_transfer_leader(
&self,
req: TransferLeaderRequest<C>,
) -> Result<TransferLeaderResponse<C>, Fatal<C>> {
if &req.to_node_id == self.inner.id()
&& let Err(err) = self.ensure_log_flushed_for_transfer_leader(&req).await?
{
return Ok(Err(err));
}
let raft_msg = RaftMsg::HandleTransferLeader {
from: req.from_leader.clone(),
to: req.to_node_id.clone(),
last_log_id: req.last_log_id.clone(),
};
self.inner.send_msg(raft_msg).await?;
Ok(Ok(()))
}
fn check_transfer_leader_progress(
req: &TransferLeaderRequest<C>,
progress: &Option<FlushPoint<C>>,
) -> TransferLeaderResponse<C> {
let progress = if let Some(progress) = progress {
progress
} else {
return Err(TransferLeaderError::LogNotFlushed {
expected: req.last_log_id.clone(),
actual: None,
});
};
let actual_vote = progress.vote.as_ref_vote();
let vote_matches = actual_vote == req.from_leader.as_ref_vote();
let log_is_flushed = progress.last_log_id.as_ref() >= req.last_log_id.as_ref();
if vote_matches && log_is_flushed {
return Ok(());
}
#[allow(clippy::neg_cmp_op_on_partial_ord)]
let vote_changed = !(req.from_leader.as_ref_vote() >= actual_vote);
if vote_changed {
let actual_vote =
VoteOf::<C>::from_leader_id(progress.vote.leader_id().clone(), progress.vote.is_committed());
return Err(TransferLeaderError::VoteChanged {
expected: req.from_leader.clone(),
actual: actual_vote,
});
}
Err(TransferLeaderError::LogNotFlushed {
expected: req.last_log_id.clone(),
actual: progress.last_log_id.clone(),
})
}
fn log_transfer_leader_progress(
req: &TransferLeaderRequest<C>,
progress: &Option<FlushPoint<C>>,
res: &TransferLeaderResponse<C>,
) {
match res {
Ok(()) => {
tracing::info!(
"Leader-transfer condition satisfied, submit Leader-transfer message; \
expected: (vote: {}, flushed_log: {})",
req.from_leader,
req.last_log_id.display(),
);
}
Err(TransferLeaderError::VoteChanged { .. }) => {
tracing::warn!(
"Vote changed, give up Leader-transfer; expected vote: {}, progress: {}",
req.from_leader,
progress.display()
);
}
Err(_) => {
tracing::warn!(
"Leader-transfer condition failed, reject Leader-transfer; \
expected: (vote: {}; flushed_log: {}), progress: {}",
req.from_leader,
req.last_log_id.display(),
progress.display()
);
}
}
}
async fn ensure_log_flushed_for_transfer_leader(
&self,
req: &TransferLeaderRequest<C>,
) -> Result<TransferLeaderResponse<C>, Fatal<C>> {
let timeout_at = C::now() + Duration::from_millis(self.inner.config().election_timeout_min);
let mut log_progress = self.inner.progress_watcher.log_progress();
loop {
let progress = log_progress.get();
let res = Self::check_transfer_leader_progress(req, &progress);
if let Ok(()) | Err(TransferLeaderError::VoteChanged { .. }) = &res {
Self::log_transfer_leader_progress(req, &progress, &res);
return Ok(res);
}
let changed_res = C::timeout_at(timeout_at, log_progress.changed()).await;
let changed_res = match changed_res {
Ok(changed_res) => changed_res,
Err(_) => {
let progress = log_progress.get();
let res = Self::check_transfer_leader_progress(req, &progress);
Self::log_transfer_leader_progress(req, &progress, &res);
return Ok(res);
}
};
if changed_res.is_err() {
return Err(self.inner.get_core_stop_error().await);
}
}
}
}