use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::error::Error;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;
use std::slice::Iter;
use std::slice::IterMut;
use validit::Validate;
use super::VecProgressEntry;
use super::VecProgressEntryData;
use super::display_vec_progress::DisplayVecProgress;
use super::progress_stats::ProgressStats;
use crate::quorum::QuorumSet;
#[derive(Clone, Debug)]
pub struct VecProgress<Entry, QS>
where
Entry: VecProgressEntry,
QS: QuorumSet<Id = Entry::Id>,
{
quorum_set: QS,
quorum_accepted: Entry::Progress,
voter_count: usize,
entries: Vec<Entry>,
stat: ProgressStats,
}
impl<Entry, QS> Display for VecProgress<Entry, QS>
where
Entry: VecProgressEntry + Display,
QS: QuorumSet<Id = Entry::Id>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{{")?;
for (i, item) in self.entries.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", item)?
}
write!(f, "}}")?;
Ok(())
}
}
impl<Entry, QS> VecProgress<Entry, QS>
where
Entry: VecProgressEntry,
Entry::Id: Ord + Clone + Debug,
Entry::Progress: Debug,
QS: QuorumSet<Id = Entry::Id>,
{
pub fn new(
quorum_set: QS,
learner_ids: impl IntoIterator<Item = Entry::Id>,
mut default_entry: impl FnMut(Entry::Id) -> Entry,
) -> Self {
let voter_ids = quorum_set.ids().collect::<BTreeSet<_>>();
let learner_ids =
learner_ids.into_iter().filter(|id| !voter_ids.contains(id)).collect::<BTreeSet<_>>();
let mut entries = voter_ids.into_iter().map(&mut default_entry).collect::<Vec<_>>();
let voter_count = entries.len();
entries.sort_by(|a, b| b.progress().cmp(a.progress()));
let mut quorum_accepted = Entry::Progress::default();
for i in 0..voter_count {
let ids = entries[..=i].iter().map(|entry| entry.id());
if quorum_set.is_quorum(ids) {
quorum_accepted = entries[i].progress().clone();
break;
}
}
entries.extend(learner_ids.into_iter().map(default_entry));
Self {
quorum_set,
quorum_accepted,
voter_count,
entries,
stat: Default::default(),
}
}
#[inline(always)]
fn index(&self, target: &Entry::Id) -> Option<usize> {
self.entries.iter().position(|item| item.id() == target)
}
#[inline(always)]
fn move_up(&mut self, index: usize) -> usize {
self.stat.move_count += 1;
for i in (0..index).rev() {
if self.entries[i].progress() < self.entries[i + 1].progress() {
self.entries.swap(i, i + 1);
} else {
return i + 1;
}
}
0
}
fn move_down(&mut self, index: usize) -> usize {
self.stat.move_count += 1;
let mut i = index;
while i + 1 < self.voter_count
&& self.entries[i].progress() < self.entries[i + 1].progress()
{
self.entries.swap(i, i + 1);
i += 1;
}
i
}
pub fn iter_mut_without_reorder(&mut self) -> IterMut<'_, Entry> {
self.entries.iter_mut()
}
#[cfg(test)]
pub(crate) fn stat(&self) -> &ProgressStats {
&self.stat
}
pub fn display_with<Fmt>(&self, f: Fmt) -> DisplayVecProgress<'_, Entry, QS, Fmt>
where Fmt: Fn(&mut Formatter<'_>, &Entry) -> std::fmt::Result {
DisplayVecProgress { inner: self, f }
}
fn debug_assert_progress_valid(&self) {
#[cfg(debug_assertions)]
self.validate().expect("VecProgress progress invariant violation");
}
}
impl<Entry, QS> VecProgress<Entry, QS>
where
Entry: VecProgressEntry,
Entry::Id: Ord + Clone + Debug,
Entry::Progress: Debug,
QS: QuorumSet<Id = Entry::Id>,
{
fn update_progress_with<F>(&mut self, id: &Entry::Id, f: F) -> Option<&Entry::Progress>
where F: FnOnce(&mut Entry::Progress) {
self.update_entry_with(id, |entry| f(entry.progress_mut()))
}
pub fn update_entry_with<F>(&mut self, id: &Entry::Id, f: F) -> Option<&Entry::Progress>
where F: FnOnce(&mut Entry) {
self.stat.update_count += 1;
let index = self.index(id)?;
let prev_progress = self.entries[index].progress().clone();
f(&mut self.entries[index]);
debug_assert!(self.entries[index].id() == id);
Some(self.update_at(index, prev_progress))
}
pub fn update_data_with<F>(&mut self, id: &Entry::Id, f: F) -> Option<&Entry::Data>
where
Entry: VecProgressEntryData,
F: FnOnce(&mut Entry::Data),
{
let index = self.index(id)?;
f(self.entries[index].data_mut());
Some(self.entries[index].data())
}
pub fn reset_entry_with<F>(&mut self, id: &Entry::Id, f: F) -> Option<&Entry>
where F: FnOnce(&mut Entry) {
let index = self.index(id)?;
let prev_progress = self.entries[index].progress().clone();
f(&mut self.entries[index]);
debug_assert!(self.entries[index].id() == id);
debug_assert!(self.entries[index].progress() <= &prev_progress);
let new_index =
if index < self.voter_count && self.entries[index].progress() < &prev_progress {
self.move_down(index)
} else {
index
};
self.debug_assert_progress_valid();
Some(&self.entries[new_index])
}
fn update_at(&mut self, index: usize, prev_progress: Entry::Progress) -> &Entry::Progress {
debug_assert!(self.entries[index].progress() >= &prev_progress,);
if &prev_progress == self.entries[index].progress() {
self.debug_assert_progress_valid();
return &self.quorum_accepted;
}
if index >= self.voter_count {
self.debug_assert_progress_valid();
return &self.quorum_accepted;
}
let prev_le_qa = prev_progress <= self.quorum_accepted;
let new_gt_qa = self.entries[index].progress() > &self.quorum_accepted;
if new_gt_qa {
let new_index = self.move_up(index);
if prev_le_qa {
for i in new_index..self.voter_count {
let prog = self.entries[i].progress();
if prog <= &self.quorum_accepted {
break;
}
let it = self.entries[0..=i].iter().map(|item| item.id());
self.stat.is_quorum_count += 1;
if self.quorum_set.is_quorum(it) {
self.quorum_accepted = prog.clone();
break;
}
}
}
}
self.debug_assert_progress_valid();
&self.quorum_accepted
}
pub fn update_progress(
&mut self,
id: &Entry::Id,
value: Entry::Progress,
) -> Option<&Entry::Progress> {
self.update_progress_with(id, |x| *x = value)
}
pub fn increase_to(
&mut self,
id: &Entry::Id,
value: Entry::Progress,
) -> Option<&Entry::Progress> {
self.update_progress_with(id, |x| {
if value > *x {
*x = value;
}
})
}
pub fn try_get(&self, id: &Entry::Id) -> Option<&Entry> {
let index = self.index(id)?;
Some(&self.entries[index])
}
pub fn quorum_accepted(&self) -> &Entry::Progress {
&self.quorum_accepted
}
pub fn quorum_set(&self) -> &QS {
&self.quorum_set
}
pub fn voter_count(&self) -> usize {
self.voter_count
}
pub fn iter(&self) -> Iter<'_, Entry> {
self.entries.as_slice().iter()
}
pub fn collect_mapped<F, T, C>(&self, f: F) -> C
where
F: Fn(&Entry) -> T,
C: FromIterator<T>,
{
self.iter().map(f).collect()
}
pub fn upgrade_quorum_set(
self,
quorum_set: QS,
learner_ids: impl IntoIterator<Item = Entry::Id>,
mut default_entry: impl FnMut(Entry::Id) -> Entry,
) -> Self {
let mut old = self
.entries
.into_iter()
.map(|entry| (entry.id().clone(), entry))
.collect::<BTreeMap<_, _>>();
let mut new_prog = Self::new(quorum_set, learner_ids, |id| {
old.remove(&id).unwrap_or_else(|| default_entry(id))
});
new_prog.stat = self.stat;
new_prog
}
pub fn is_voter(&self, id: &Entry::Id) -> Option<bool> {
let index = self.index(id)?;
Some(index < self.voter_count)
}
}
impl<Entry, QS> IntoIterator for VecProgress<Entry, QS>
where
Entry: VecProgressEntry,
QS: QuorumSet<Id = Entry::Id>,
{
type Item = Entry;
type IntoIter = std::vec::IntoIter<Entry>;
fn into_iter(self) -> Self::IntoIter {
self.entries.into_iter()
}
}
impl<Entry, QS> Validate for VecProgress<Entry, QS>
where
Entry: VecProgressEntry,
Entry::Id: Ord + Clone + Debug,
Entry::Progress: Debug,
QS: QuorumSet<Id = Entry::Id>,
{
fn validate(&self) -> Result<(), Box<dyn Error>> {
self.validate_voter_order()
}
}
impl<Entry, QS> VecProgress<Entry, QS>
where
Entry: VecProgressEntry,
Entry::Id: Ord + Clone + Debug,
Entry::Progress: Debug,
QS: QuorumSet<Id = Entry::Id>,
{
fn validate_voter_order(&self) -> Result<(), Box<dyn Error>> {
let voters = &self.entries[..self.voter_count];
let progress_state = || {
let voter_progress = voters
.iter()
.map(|entry| (entry.id().clone(), entry.progress().clone()))
.collect::<Vec<_>>();
let learner_progress = self.entries[self.voter_count..]
.iter()
.map(|entry| (entry.id().clone(), entry.progress().clone()))
.collect::<Vec<_>>();
(voter_progress, learner_progress)
};
let suffix_start = voters
.iter()
.position(|entry| entry.progress() <= &self.quorum_accepted)
.unwrap_or(voters.len());
for (previous_index, pair) in voters[..suffix_start].windows(2).enumerate() {
let previous = &pair[0];
let item = &pair[1];
if previous.progress() < item.progress() {
let (voter_progress, learner_progress) = progress_state();
return Err(format!(
"voter progress above quorum_accepted is not descending: quorum_accepted={:?}, previous_entry={:?}, out_of_order_entry={:?}, voter_progress={voter_progress:?}, learner_progress={learner_progress:?}",
self.quorum_accepted,
(previous_index, previous.id(), previous.progress()),
(previous_index + 1, item.id(), item.progress())
)
.into());
}
}
for (suffix_offset, item) in voters[suffix_start..].iter().enumerate() {
if item.progress() <= &self.quorum_accepted {
continue;
}
let index = suffix_start + suffix_offset;
let (voter_progress, learner_progress) = progress_state();
return Err(format!(
"voter progress above quorum_accepted appears after the unsorted suffix: quorum_accepted={:?}, out_of_order_entry={:?}, voter_progress={voter_progress:?}, learner_progress={learner_progress:?}",
self.quorum_accepted,
(index, item.id(), item.progress())
)
.into());
}
Ok(())
}
}
#[cfg(test)]
mod vec_progress_test;