use std::ops::RangeInclusive;
use num_traits::Bounded;
use num_traits::Zero;
use thiserror::Error;
use crate::error::ShutDownOr;
use crate::invocation::AbstainOf;
use crate::invocation::CommunicationErrorOf;
use crate::invocation::Invocation;
use crate::invocation::NayOf;
use crate::invocation::RoundNumOf;
use crate::retry::DoNotRetry;
use crate::retry::RetryPolicy;
#[derive(Debug)]
pub struct AppendArgs<I: Invocation, R = DoNotRetry<I>> {
pub round: RangeInclusive<RoundNumOf<I>>,
pub importance: Importance,
pub retry_policy: R,
}
impl<I: Invocation, R: RetryPolicy<Invocation = I> + Default> Default for AppendArgs<I, R> {
fn default() -> Self {
R::default().into()
}
}
impl<I: Invocation, R: Clone> Clone for AppendArgs<I, R> {
fn clone(&self) -> Self {
Self {
round: self.round.clone(),
importance: self.importance,
retry_policy: self.retry_policy.clone(),
}
}
}
impl<I, R> From<R> for AppendArgs<I, R>
where
I: Invocation,
R: RetryPolicy<Invocation = I>,
{
fn from(retry_policy: R) -> Self {
Self {
round: Zero::zero()..=Bounded::max_value(),
importance: Importance::GainLeadership,
retry_policy,
}
}
}
impl<I: Invocation> From<()> for AppendArgs<I> {
fn from(_: ()) -> Self {
Default::default()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Importance {
GainLeadership,
MaintainLeadership(Peeryness),
}
impl Default for Importance {
fn default() -> Self {
Self::GainLeadership
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Peeryness {
Peery,
Unpeery,
}
#[derive(Error)]
#[non_exhaustive]
pub enum AppendError<I: Invocation> {
#[error("round had already converged")]
Converged {
caught_up: bool,
},
#[error("a node decoration raised an error")]
Decoration(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
#[error("node is disoriented")]
Disoriented,
#[error("node was removed from the cluster")]
Exiled,
#[error("node lost its mandate or failed in acquiring one")]
Lost,
#[error("node could not achieve a quorum")]
NoQuorum {
abstentions: Vec<AbstainOf<I>>,
communication_errors: Vec<CommunicationErrorOf<I>>,
discards: Vec<NayOf<I>>,
rejections: Vec<NayOf<I>>,
},
#[error("node is passive")]
Passive,
#[error("node was forced to append a different entry")]
Railroaded,
#[error("node is shut down")]
ShutDown,
#[error("entry is unacceptable")]
Unacceptable(NayOf<I>),
}
impl<I: Invocation> From<ShutDownOr<AppendError<I>>> for AppendError<I> {
fn from(e: ShutDownOr<AppendError<I>>) -> Self {
match e {
ShutDownOr::Other(e) => e,
ShutDownOr::ShutDown => AppendError::ShutDown,
}
}
}
impl<I: Invocation> std::fmt::Debug for AppendError<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AppendError::Converged { caught_up } => f
.debug_struct("AppendError::Converged")
.field("caught_up", caught_up)
.finish(),
AppendError::Decoration(err) => {
f.debug_tuple("AppendError::Decoration").field(err).finish()
}
AppendError::Disoriented => f.debug_tuple("AppendError::Disoriented").finish(),
AppendError::Exiled => f.debug_tuple("AppendError::Exiled").finish(),
AppendError::Lost => f.debug_tuple("AppendError::Lost").finish(),
AppendError::NoQuorum {
abstentions,
communication_errors,
discards,
rejections,
} => f
.debug_struct("AppendError::NoQuorum")
.field("abstentions", abstentions)
.field("communication_errors", communication_errors)
.field("discards", discards)
.field("rejections", rejections)
.finish(),
AppendError::Passive => f.debug_tuple("AppendError::Passive").finish(),
AppendError::Railroaded => f.debug_tuple("AppendError::Railroaded").finish(),
AppendError::ShutDown => f.debug_tuple("AppendError::ShutDown").finish(),
AppendError::Unacceptable(reason) => f
.debug_tuple("AppendError::Unacceptable")
.field(reason)
.finish(),
}
}
}