1use std::collections::BTreeMap;
2use std::collections::BTreeSet;
3use std::error::Error;
4use std::fmt::Debug;
5use std::fmt::Display;
6use std::fmt::Formatter;
7use std::slice::Iter;
8use std::slice::IterMut;
9
10use validit::Validate;
11
12use super::VecProgressEntry;
13use super::VecProgressEntryData;
14use super::display_vec_progress::DisplayVecProgress;
15use super::progress_stats::ProgressStats;
16use crate::quorum::QuorumSet;
17
18#[derive(Clone, Debug)]
31pub struct VecProgress<Entry, QS>
32where
33 Entry: VecProgressEntry,
34 QS: QuorumSet<Id = Entry::Id>,
35{
36 quorum_set: QS,
38
39 quorum_accepted: Entry::Progress,
41
42 voter_count: usize,
44
45 entries: Vec<Entry>,
54
55 stat: ProgressStats,
57}
58
59impl<Entry, QS> Display for VecProgress<Entry, QS>
60where
61 Entry: VecProgressEntry + Display,
62 QS: QuorumSet<Id = Entry::Id> + 'static,
63{
64 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
65 write!(f, "{{")?;
66 for (i, item) in self.entries.iter().enumerate() {
67 if i > 0 {
68 write!(f, ", ")?;
69 }
70 write!(f, "{}", item)?
71 }
72 write!(f, "}}")?;
73
74 Ok(())
75 }
76}
77
78impl<Entry, QS> VecProgress<Entry, QS>
79where
80 Entry: VecProgressEntry,
81 Entry::Id: Ord + Clone + Debug,
82 Entry::Progress: Debug,
83 QS: QuorumSet<Id = Entry::Id>,
84{
85 pub fn new(
91 quorum_set: QS,
92 learner_ids: impl IntoIterator<Item = Entry::Id>,
93 mut default_entry: impl FnMut(Entry::Id) -> Entry,
94 ) -> Self {
95 let mut entries = quorum_set.ids().map(&mut default_entry).collect::<Vec<_>>();
96
97 let voter_count = entries.len();
98
99 entries.extend(learner_ids.into_iter().map(default_entry));
100
101 let this = Self {
102 quorum_set,
103 quorum_accepted: Default::default(),
104 voter_count,
105 entries,
106 stat: Default::default(),
107 };
108 this.validate_initial_state().expect("VecProgress construction invariant violation");
109 this
110 }
111
112 #[inline(always)]
114 fn index(&self, target: &Entry::Id) -> Option<usize> {
115 self.entries.iter().position(|item| item.id() == target)
116 }
117
118 #[inline(always)]
120 fn move_up(&mut self, index: usize) -> usize {
121 self.stat.move_count += 1;
122 for i in (0..index).rev() {
123 if self.entries[i].progress() < self.entries[i + 1].progress() {
124 self.entries.swap(i, i + 1);
125 } else {
126 return i + 1;
127 }
128 }
129
130 0
131 }
132
133 fn move_down(&mut self, index: usize) -> usize {
138 self.stat.move_count += 1;
139 let mut i = index;
140 while i + 1 < self.voter_count
141 && self.entries[i].progress() < self.entries[i + 1].progress()
142 {
143 self.entries.swap(i, i + 1);
144 i += 1;
145 }
146
147 i
148 }
149
150 pub fn iter_mut_without_reorder(&mut self) -> IterMut<'_, Entry> {
157 self.entries.iter_mut()
158 }
159
160 #[cfg(test)]
161 pub(crate) fn stat(&self) -> &ProgressStats {
162 &self.stat
163 }
164
165 pub fn display_with<Fmt>(&self, f: Fmt) -> DisplayVecProgress<'_, Entry, QS, Fmt>
167 where Fmt: Fn(&mut Formatter<'_>, &Entry) -> std::fmt::Result {
168 DisplayVecProgress { inner: self, f }
169 }
170
171 fn debug_assert_progress_valid(&self) {
173 #[cfg(debug_assertions)]
174 self.validate().expect("VecProgress progress invariant violation");
175 }
176}
177
178impl<Entry, QS> VecProgress<Entry, QS>
179where
180 Entry: VecProgressEntry,
181 Entry::Id: Ord + Clone + Debug,
182 Entry::Progress: Debug,
183 QS: QuorumSet<Id = Entry::Id>,
184{
185 fn update_progress_with<F>(&mut self, id: &Entry::Id, f: F) -> Option<&Entry::Progress>
221 where F: FnOnce(&mut Entry::Progress) {
222 self.update_entry_with(id, |entry| f(entry.progress_mut()))
223 }
224
225 pub fn update_entry_with<F>(&mut self, id: &Entry::Id, f: F) -> Option<&Entry::Progress>
234 where F: FnOnce(&mut Entry) {
235 self.stat.update_count += 1;
236
237 let index = self.index(id)?;
238
239 let prev_progress = self.entries[index].progress().clone();
240
241 f(&mut self.entries[index]);
242
243 debug_assert!(self.entries[index].id() == id);
244
245 Some(self.update_at(index, prev_progress))
246 }
247
248 pub fn update_data_with<F>(&mut self, id: &Entry::Id, f: F) -> Option<&Entry::Data>
255 where
256 Entry: VecProgressEntryData,
257 F: FnOnce(&mut Entry::Data),
258 {
259 let index = self.index(id)?;
260
261 f(self.entries[index].data_mut());
262
263 Some(self.entries[index].data())
264 }
265
266 pub fn reset_entry_with<F>(&mut self, id: &Entry::Id, f: F) -> Option<&Entry>
277 where F: FnOnce(&mut Entry) {
278 let index = self.index(id)?;
279
280 let prev_progress = self.entries[index].progress().clone();
281
282 f(&mut self.entries[index]);
283
284 debug_assert!(self.entries[index].id() == id);
285 debug_assert!(self.entries[index].progress() <= &prev_progress);
286
287 let new_index =
289 if index < self.voter_count && self.entries[index].progress() < &prev_progress {
290 self.move_down(index)
291 } else {
292 index
293 };
294
295 self.debug_assert_progress_valid();
296 Some(&self.entries[new_index])
297 }
298
299 fn update_at(&mut self, index: usize, prev_progress: Entry::Progress) -> &Entry::Progress {
300 debug_assert!(self.entries[index].progress() >= &prev_progress,);
301
302 if &prev_progress == self.entries[index].progress() {
304 self.debug_assert_progress_valid();
305 return &self.quorum_accepted;
306 }
307
308 if index >= self.voter_count {
311 self.debug_assert_progress_valid();
312 return &self.quorum_accepted;
313 }
314
315 let prev_le_qa = prev_progress <= self.quorum_accepted;
316 let new_gt_qa = self.entries[index].progress() > &self.quorum_accepted;
317
318 if new_gt_qa {
321 let new_index = self.move_up(index);
322
323 if prev_le_qa {
324 for i in new_index..self.voter_count {
326 let prog = self.entries[i].progress();
327
328 if prog <= &self.quorum_accepted {
330 break;
331 }
332
333 let it = self.entries[0..=i].iter().map(|item| item.id());
335
336 self.stat.is_quorum_count += 1;
337
338 if self.quorum_set.is_quorum(it) {
339 self.quorum_accepted = prog.clone();
340 break;
341 }
342 }
343 }
344 }
345
346 self.debug_assert_progress_valid();
347 &self.quorum_accepted
348 }
349
350 pub fn update_progress(
358 &mut self,
359 id: &Entry::Id,
360 value: Entry::Progress,
361 ) -> Option<&Entry::Progress> {
362 self.update_progress_with(id, |x| *x = value)
363 }
364
365 pub fn increase_to(
370 &mut self,
371 id: &Entry::Id,
372 value: Entry::Progress,
373 ) -> Option<&Entry::Progress> {
374 self.update_progress_with(id, |x| {
375 if value > *x {
376 *x = value;
377 }
378 })
379 }
380
381 pub fn get(&self, id: &Entry::Id) -> Option<&Entry> {
383 let index = self.index(id)?;
384 Some(&self.entries[index])
385 }
386
387 pub fn quorum_accepted(&self) -> &Entry::Progress {
392 &self.quorum_accepted
393 }
394
395 pub fn iter(&self) -> Iter<'_, Entry> {
397 self.entries.as_slice().iter()
398 }
399
400 pub fn collect_mapped<F, T, C>(&self, f: F) -> C
402 where
403 F: Fn(&Entry) -> T,
404 C: FromIterator<T>,
405 {
406 self.iter().map(f).collect()
407 }
408
409 pub fn upgrade_quorum_set(
415 self,
416 quorum_set: QS,
417 learner_ids: impl IntoIterator<Item = Entry::Id>,
418 default_entry: impl FnMut(Entry::Id) -> Entry,
419 ) -> Self {
420 let mut new_prog = Self::new(quorum_set, learner_ids, default_entry);
421
422 new_prog.stat = self.stat.clone();
423
424 for item in self.into_iter() {
425 new_prog.replace(item);
426 }
427 new_prog.debug_assert_progress_valid();
428 new_prog
429 }
430
431 fn replace(&mut self, entry: Entry) -> Option<&Entry::Progress> {
433 self.stat.update_count += 1;
434
435 let index = self.index(entry.id())?;
436
437 let prev_progress = self.entries[index].progress().clone();
438
439 self.entries[index] = entry;
440
441 Some(self.update_at(index, prev_progress))
442 }
443
444 pub fn is_voter(&self, id: &Entry::Id) -> Option<bool> {
451 let index = self.index(id)?;
452 Some(index < self.voter_count)
453 }
454}
455
456impl<Entry, QS> IntoIterator for VecProgress<Entry, QS>
457where
458 Entry: VecProgressEntry,
459 QS: QuorumSet<Id = Entry::Id>,
460{
461 type Item = Entry;
462 type IntoIter = std::vec::IntoIter<Entry>;
463
464 fn into_iter(self) -> Self::IntoIter {
465 self.entries.into_iter()
466 }
467}
468
469impl<Entry, QS> Validate for VecProgress<Entry, QS>
470where
471 Entry: VecProgressEntry,
472 Entry::Id: Ord + Clone + Debug,
473 Entry::Progress: Debug,
474 QS: QuorumSet<Id = Entry::Id>,
475{
476 fn validate(&self) -> Result<(), Box<dyn Error>> {
478 self.validate_progress()
479 }
480}
481
482impl<Entry, QS> VecProgress<Entry, QS>
483where
484 Entry: VecProgressEntry,
485 Entry::Id: Ord + Clone + Debug,
486 Entry::Progress: Debug,
487 QS: QuorumSet<Id = Entry::Id>,
488{
489 fn validate_initial_state(&self) -> Result<(), Box<dyn Error>> {
491 self.validate_voter_count()?;
492 self.validate_unique_entry_ids()?;
493 self.validate_quorum_membership()?;
494 self.validate_progress()?;
495
496 Ok(())
497 }
498
499 fn validate_progress(&self) -> Result<(), Box<dyn Error>> {
501 self.validate_voter_order()
502 }
503
504 fn validate_voter_count(&self) -> Result<(), Box<dyn Error>> {
507 if self.voter_count > self.entries.len() {
508 return Err(invalid(format!(
509 "voter_count {} exceeds entry count {}",
510 self.voter_count,
511 self.entries.len()
512 )));
513 }
514
515 Ok(())
516 }
517
518 fn validate_unique_entry_ids(&self) -> Result<(), Box<dyn Error>> {
521 let mut positions = BTreeMap::<Entry::Id, Vec<usize>>::new();
522 for (i, entry) in self.entries.iter().enumerate() {
523 positions.entry(entry.id().clone()).or_default().push(i);
524 }
525
526 let duplicates = positions
527 .into_iter()
528 .filter(|(_, positions)| positions.len() > 1)
529 .collect::<BTreeMap<_, _>>();
530
531 if !duplicates.is_empty() {
532 return Err(invalid(format!("duplicate entry ids: {duplicates:?}")));
533 }
534
535 Ok(())
536 }
537
538 fn validate_quorum_membership(&self) -> Result<(), Box<dyn Error>> {
541 let quorum_ids = self.quorum_set.ids().collect::<BTreeSet<_>>();
542 let voter_entry_ids = self.entries[..self.voter_count]
543 .iter()
544 .map(|entry| entry.id().clone())
545 .collect::<BTreeSet<_>>();
546 let learner_entry_ids = self.entries[self.voter_count..]
547 .iter()
548 .map(|entry| entry.id().clone())
549 .collect::<BTreeSet<_>>();
550
551 let missing_voter_ids =
552 quorum_ids.difference(&voter_entry_ids).cloned().collect::<BTreeSet<_>>();
553 let extra_voter_ids =
554 voter_entry_ids.difference(&quorum_ids).cloned().collect::<BTreeSet<_>>();
555 let learner_voter_ids =
556 learner_entry_ids.intersection(&quorum_ids).cloned().collect::<BTreeSet<_>>();
557
558 if quorum_ids.len() == self.voter_count
559 && missing_voter_ids.is_empty()
560 && extra_voter_ids.is_empty()
561 && learner_voter_ids.is_empty()
562 {
563 return Ok(());
564 }
565
566 Err(invalid(format!(
567 "quorum membership mismatch: quorum_ids={quorum_ids:?}, voter_entry_ids={voter_entry_ids:?}, learner_entry_ids={learner_entry_ids:?}, missing_voter_ids={missing_voter_ids:?}, extra_voter_ids={extra_voter_ids:?}, learner_voter_ids={learner_voter_ids:?}, voter_count={}",
568 self.voter_count
569 )))
570 }
571
572 fn validate_voter_order(&self) -> Result<(), Box<dyn Error>> {
576 let voters = &self.entries[..self.voter_count];
577 let progress_state = || {
578 let voter_progress = voters
579 .iter()
580 .map(|entry| (entry.id().clone(), entry.progress().clone()))
581 .collect::<Vec<_>>();
582 let learner_progress = self.entries[self.voter_count..]
583 .iter()
584 .map(|entry| (entry.id().clone(), entry.progress().clone()))
585 .collect::<Vec<_>>();
586
587 (voter_progress, learner_progress)
588 };
589
590 let suffix_start = voters
591 .iter()
592 .position(|entry| entry.progress() <= &self.quorum_accepted)
593 .unwrap_or(voters.len());
594
595 for (previous_index, pair) in voters[..suffix_start].windows(2).enumerate() {
596 let previous = &pair[0];
597 let item = &pair[1];
598 if previous.progress() < item.progress() {
599 let (voter_progress, learner_progress) = progress_state();
600 return Err(invalid(format!(
601 "voter progress above quorum_accepted is not descending: quorum_accepted={:?}, previous_entry={:?}, out_of_order_entry={:?}, voter_progress={voter_progress:?}, learner_progress={learner_progress:?}",
602 self.quorum_accepted,
603 (previous_index, previous.id(), previous.progress()),
604 (previous_index + 1, item.id(), item.progress())
605 )));
606 }
607 }
608
609 for (suffix_offset, item) in voters[suffix_start..].iter().enumerate() {
610 if item.progress() <= &self.quorum_accepted {
611 continue;
612 }
613
614 let index = suffix_start + suffix_offset;
615 let (voter_progress, learner_progress) = progress_state();
616 return Err(invalid(format!(
617 "voter progress above quorum_accepted appears after the unsorted suffix: quorum_accepted={:?}, out_of_order_entry={:?}, voter_progress={voter_progress:?}, learner_progress={learner_progress:?}",
618 self.quorum_accepted,
619 (index, item.id(), item.progress())
620 )));
621 }
622
623 Ok(())
624 }
625}
626
627fn invalid(message: impl Into<String>) -> Box<dyn Error> {
628 Box::new(std::io::Error::new(
629 std::io::ErrorKind::InvalidData,
630 message.into(),
631 ))
632}
633
634#[cfg(test)]
635mod tests {
636 use std::collections::BTreeMap;
637 use std::collections::BTreeSet;
638
639 use maplit::btreeset;
640 use validit::Validate;
641
642 use super::VecProgress;
643 use crate::Node;
644 use crate::QuorumTree;
645 use crate::progress::VecProgressEntry;
646 use crate::progress::VecProgressEntryData;
647 use crate::quorum::QuorumSet;
648
649 const LCG_A: u64 = 6364136223846793005;
650 const LCG_C: u64 = 1442695040888963407;
651
652 #[derive(Clone, Debug, PartialEq, Eq)]
653 struct IdValData<ID, Val, Data> {
654 id: ID,
655 val: Val,
656 data: Data,
657 }
658
659 impl<ID, Val, Data> IdValData<ID, Val, Data> {
660 fn new(id: ID, val: Val, data: Data) -> Self {
661 Self { id, val, data }
662 }
663 }
664
665 impl<ID, Val, Data> VecProgressEntry for IdValData<ID, Val, Data>
666 where
667 ID: 'static + PartialEq,
668 Val: Clone + Default + Ord,
669 {
670 type Id = ID;
671 type Progress = Val;
672
673 fn id(&self) -> &Self::Id {
674 &self.id
675 }
676
677 fn progress(&self) -> &Self::Progress {
678 &self.val
679 }
680
681 fn progress_mut(&mut self) -> &mut Self::Progress {
682 &mut self.val
683 }
684 }
685
686 impl<ID, Val, Data> VecProgressEntryData for IdValData<ID, Val, Data>
687 where
688 ID: 'static + PartialEq,
689 Val: Clone + Default + Ord,
690 {
691 type Data = Data;
692
693 fn data(&self) -> &Self::Data {
694 &self.data
695 }
696
697 fn data_mut(&mut self) -> &mut Self::Data {
698 &mut self.data
699 }
700 }
701
702 #[derive(Clone, Debug)]
703 struct RequiredSetQuorum {
704 ids: BTreeSet<u64>,
705 required: BTreeSet<u64>,
706 }
707
708 impl RequiredSetQuorum {
709 fn new(
714 ids: impl IntoIterator<Item = u64>,
715 required: impl IntoIterator<Item = u64>,
716 ) -> Self {
717 Self {
718 ids: ids.into_iter().collect(),
719 required: required.into_iter().collect(),
720 }
721 }
722 }
723
724 impl QuorumSet for RequiredSetQuorum {
725 type Id = u64;
726
727 type Iter = std::collections::btree_set::IntoIter<u64>;
728
729 fn is_quorum<'a, I: Iterator<Item = &'a Self::Id> + Clone>(&self, ids: I) -> bool {
730 let granted = ids.copied().collect::<BTreeSet<_>>();
731 self.required.is_subset(&granted)
732 }
733
734 fn ids(&self) -> Self::Iter {
735 self.ids.clone().into_iter()
736 }
737 }
738
739 fn next_random(seed: &mut u64) -> u64 {
744 *seed = seed.wrapping_mul(LCG_A).wrapping_add(LCG_C);
745 *seed
746 }
747
748 fn learner_ids_for<QS>(quorum_set: &QS, known_ids: impl IntoIterator<Item = u64>) -> Vec<u64>
753 where QS: QuorumSet<Id = u64> {
754 let voter_ids = quorum_set.ids().collect::<BTreeSet<_>>();
755 known_ids.into_iter().filter(|id| !voter_ids.contains(id)).collect()
756 }
757
758 fn copy_option(res: Option<&u64>) -> Option<u64> {
760 res.copied()
761 }
762
763 fn model_quorum_accepted<QS>(quorum_set: &QS, entries: &[(u64, u64)]) -> u64
769 where QS: QuorumSet<Id = u64> {
770 let values = entries.iter().map(|item| (item.0, item.1)).collect::<BTreeMap<_, _>>();
771 let mut candidates = quorum_set.ids().map(|id| values[&id]).collect::<Vec<_>>();
772
773 candidates.sort_unstable_by(|a, b| b.cmp(a));
774 candidates.dedup();
775
776 for candidate in candidates {
777 let ids = values.iter().filter_map(|(id, val)| (*val >= candidate).then_some(id));
778 if quorum_set.is_quorum(ids) {
779 return candidate;
780 }
781 }
782
783 0
784 }
785
786 fn assert_matches_model<QS>(progress: &VecProgress<(u64, u64), QS>, context: &str)
791 where QS: QuorumSet<Id = u64> {
792 let want = model_quorum_accepted(&progress.quorum_set, &progress.entries);
793 assert_eq!(
794 &want,
795 progress.quorum_accepted(),
796 "{}: entries: {:?}",
797 context,
798 progress.entries
799 );
800 assert_voter_prefix_is_sorted(progress, context);
801 }
802
803 fn assert_voter_prefix_is_sorted<QS>(progress: &VecProgress<(u64, u64), QS>, context: &str)
808 where QS: QuorumSet<Id = u64> {
809 let quorum_accepted = *progress.quorum_accepted();
810 let mut previous = None;
811 let mut seen_unsorted_suffix = false;
812
813 for item in &progress.entries[..progress.voter_count] {
814 if item.1 <= quorum_accepted {
815 seen_unsorted_suffix = true;
816 continue;
817 }
818
819 assert!(
820 !seen_unsorted_suffix,
821 "{}: non-prefix above-quorum entry: {:?}",
822 context, progress.entries
823 );
824 if let Some(prev) = previous {
825 assert!(
826 prev >= item.1,
827 "{}: unsorted voters: {:?}",
828 context,
829 progress.entries
830 );
831 }
832 previous = Some(item.1);
833 }
834 }
835
836 fn assert_initial_invalid_contains<QS>(progress: &VecProgress<(u64, u64), QS>, want: &str)
837 where QS: QuorumSet<Id = u64> {
838 let err = progress.validate_initial_state().unwrap_err().to_string();
839 assert!(err.contains(want), "error: {err}");
840 }
841
842 fn assert_err_contains(err: Box<dyn std::error::Error>, want: &str) {
843 let err = err.to_string();
844 assert!(err.contains(want), "error: {err}");
845 }
846
847 #[test]
848 fn vec_progress_new() {
849 let quorum_set = vec![btreeset! {0, 1, 2, 3, 4}];
850 let progress = VecProgress::<(u64, u64), _>::new(quorum_set, [6, 7], |id| (id, 0));
851
852 assert_eq!(
853 vec![(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (6, 0), (7, 0),],
854 progress.entries
855 );
856 assert_eq!(5, progress.voter_count);
857 }
858
859 #[test]
860 fn vec_progress_new_with_tree() {
861 let group_a = QuorumTree::new(2, [Node::Id(1), Node::Id(2), Node::Id(3)]).unwrap();
862 let group_b = QuorumTree::new(2, [Node::Id(4), Node::Id(5), Node::Id(6)]).unwrap();
863 let quorum_set =
864 QuorumTree::new(2, [Node::Subtree(group_a), Node::Subtree(group_b)]).unwrap();
865 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [], |id| (id, 0));
866
867 assert_eq!(
868 vec![(1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0)],
869 progress.entries
870 );
871 assert_eq!(6, progress.voter_count);
872
873 assert_eq!(Some(&0), progress.update_progress(&1, 10));
874 assert_eq!(Some(&0), progress.update_progress(&2, 10));
875 assert_eq!(Some(&0), progress.update_progress(&4, 10));
876 assert_eq!(Some(&10), progress.update_progress(&5, 10));
877 }
878
879 #[test]
880 fn vec_progress_validate_rejects_duplicate_entry_ids() {
881 let quorum_set = vec![btreeset! {0, 1, 2}];
882 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [3], |id| (id, 0));
883
884 progress.entries[3].0 = 1;
885
886 assert!(progress.validate().is_ok());
887 assert_initial_invalid_contains(&progress, "duplicate entry id");
888 assert_initial_invalid_contains(&progress, "1: [1, 3]");
889 }
890
891 #[test]
892 fn vec_progress_validate_reports_membership_mismatches() {
893 let quorum_set = vec![btreeset! {0, 1, 2}];
894 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [3, 4], |id| (id, 0));
895
896 progress.entries[0].0 = 9;
897 progress.entries[3].0 = 2;
898
899 let err = progress.validate_quorum_membership().unwrap_err();
900 assert_err_contains(err, "missing_voter_ids={0}");
901
902 let err = progress.validate_quorum_membership().unwrap_err();
903 assert_err_contains(err, "extra_voter_ids={9}");
904
905 let err = progress.validate_quorum_membership().unwrap_err();
906 assert_err_contains(err, "learner_voter_ids={2}");
907 }
908
909 #[test]
910 fn vec_progress_validate_reports_voter_order_mismatches() {
911 let quorum_set = vec![btreeset! {0, 1, 2}];
912 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [3], |id| (id, 0));
913
914 progress.quorum_accepted = 4;
915 progress.entries[0].1 = 7;
916 progress.entries[1].1 = 3;
917 progress.entries[2].1 = 6;
918 progress.entries[3].1 = 9;
919
920 let err = progress.validate_voter_order().unwrap_err().to_string();
921 assert!(
922 err.contains("appears after the unsorted suffix"),
923 "error: {err}"
924 );
925 assert!(err.contains("out_of_order_entry=(2, 2, 6)"), "error: {err}");
926 assert!(
927 err.contains("voter_progress=[(0, 7), (1, 3), (2, 6)]"),
928 "error: {err}"
929 );
930 assert!(err.contains("learner_progress=[(3, 9)]"), "error: {err}");
931
932 let quorum_set = vec![btreeset! {0, 1, 2}];
933 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [3], |id| (id, 0));
934
935 progress.quorum_accepted = 4;
936 progress.entries[0].1 = 5;
937 progress.entries[1].1 = 6;
938 progress.entries[3].1 = 9;
939
940 let err = progress.validate_voter_order().unwrap_err().to_string();
941 assert!(err.contains("previous_entry=(0, 0, 5)"), "error: {err}");
942 assert!(err.contains("out_of_order_entry=(1, 1, 6)"), "error: {err}");
943 assert!(
944 err.contains("voter_progress=[(0, 5), (1, 6), (2, 0)]"),
945 "error: {err}"
946 );
947 assert!(err.contains("learner_progress=[(3, 9)]"), "error: {err}");
948 }
949
950 #[test]
951 fn vec_progress_validate_accepts_reset_below_quorum_accepted() {
952 let quorum_set = vec![btreeset! {0, 1, 2}];
953 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [], |id| (id, 0));
954
955 progress.update_progress(&0, 10).unwrap();
956 progress.update_progress(&1, 10).unwrap();
957 progress.reset_entry_with(&0, |entry| entry.1 = 0).unwrap();
958
959 assert!(progress.validate().is_ok());
960 }
961
962 #[test]
963 fn vec_progress_tuple_entry() {
964 let quorum_set = vec![btreeset! {0, 1, 2}];
965 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [3], |id| (id, 0));
966
967 assert_eq!(
968 vec![(0, 0), (1, 0), (2, 0), (3, 0)],
969 progress.iter().cloned().collect::<Vec<_>>()
970 );
971 assert_eq!(Some(&0), progress.update_progress(&0, 5));
972 assert_eq!(Some(&5), progress.update_progress(&1, 5));
973 assert_eq!(Some(&(0, 5)), progress.get(&0));
974 assert_eq!(Some(&(1, 5)), progress.get(&1));
975 assert_eq!(&5, progress.quorum_accepted());
976 }
977
978 #[test]
979 fn vec_progress_index() {
980 let quorum_set = vec![btreeset! {0, 1, 2, 3, 4}];
981 let progress = VecProgress::<(u64, u64), _>::new(quorum_set, [6, 7], |id| (id, 0));
982
983 assert_eq!(Some(0), progress.index(&0));
984 assert_eq!(Some(1), progress.index(&1));
985 assert_eq!(Some(4), progress.index(&4));
986 assert_eq!(Some(5), progress.index(&6));
987 assert_eq!(Some(6), progress.index(&7));
988 assert_eq!(None, progress.index(&9));
989 assert_eq!(None, progress.index(&100));
990 }
991
992 #[test]
993 fn vec_progress_get() {
994 let quorum_set = vec![btreeset! {0, 1, 2, 3, 4}];
995 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [6, 7], |id| (id, 0));
996
997 progress.update_progress(&6, 5);
998 assert_eq!(Some(&(6, 5)), progress.get(&6));
999 assert_eq!(Some(&5), progress.get(&6).map(|x| &x.1));
1000 assert_eq!(None, progress.get(&9));
1001
1002 progress.update_progress(&6, 10);
1003 assert_eq!(Some(&10), progress.get(&6).map(|x| &x.1));
1004 }
1005
1006 #[test]
1007 fn vec_progress_iter() {
1008 let quorum_set = vec![btreeset! {0, 1, 2, 3, 4}];
1009 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [6, 7], |id| (id, 0));
1010
1011 progress.update_progress(&7, 7);
1012 progress.update_progress(&3, 3);
1013 progress.update_progress(&1, 1);
1014
1015 assert_eq!(
1016 vec![(3, 3), (1, 1), (0, 0), (2, 0), (4, 0), (6, 0), (7, 7),],
1017 progress.iter().cloned().collect::<Vec<_>>(),
1018 "iter() returns voter first, followed by learners"
1019 );
1020 }
1021
1022 #[test]
1023 fn vec_progress_move_up() {
1024 let quorum_set = vec![btreeset! {0, 1, 2, 3, 4}];
1025 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [6], |id| (id, 0));
1026
1027 let cases = [
1029 (
1030 (1, 2),
1031 vec![(1, 2), (0, 0), (2, 0), (3, 0), (4, 0), (6, 0)],
1032 0,
1033 ),
1034 (
1035 (2, 3),
1036 vec![(2, 3), (1, 2), (0, 0), (3, 0), (4, 0), (6, 0)],
1037 0,
1038 ),
1039 (
1040 (1, 3),
1041 vec![(2, 3), (1, 3), (0, 0), (3, 0), (4, 0), (6, 0)],
1042 1,
1043 ), (
1045 (4, 8),
1046 vec![(4, 8), (2, 3), (1, 3), (0, 0), (3, 0), (6, 0)],
1047 0,
1048 ),
1049 (
1050 (0, 5),
1051 vec![(4, 8), (0, 5), (2, 3), (1, 3), (3, 0), (6, 0)],
1052 1,
1053 ), ];
1055 for (ith, ((id, v), want_vec, want_new_index)) in cases.iter().enumerate() {
1056 let index = progress.index(id).unwrap();
1058 progress.entries[index].1 = *v;
1059 let got = progress.move_up(index);
1060
1061 assert_eq!(
1062 want_vec, &progress.entries,
1063 "{}-th case: idx:{}, v:{}",
1064 ith, *id, *v
1065 );
1066 assert_eq!(
1067 *want_new_index, got,
1068 "{}-th case: idx:{}, v:{}",
1069 ith, *id, *v
1070 );
1071 }
1072 }
1073
1074 #[test]
1075 fn vec_progress_update_progress() {
1076 let quorum_set = vec![btreeset! {0, 1, 2, 3, 4}];
1077 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [6], |id| (id, 0));
1078
1079 let cases = vec![
1081 ((6, 9), Some(&0)), ((1, 2), Some(&0)), ((2, 3), Some(&0)), ((3, 1), Some(&1)), ((4, 5), Some(&2)), ((0, 4), Some(&3)), ((3, 2), Some(&3)), ((3, 3), Some(&3)), ((1, 4), Some(&4)), ((9, 1), None), ];
1092
1093 for (ith, ((id, v), want_quorum_accepted)) in cases.iter().enumerate() {
1094 let got = progress.update_progress_with(id, |x| *x = *v);
1095 assert_eq!(
1096 want_quorum_accepted.clone(),
1097 got,
1098 "{}-th case: id:{}, v:{}",
1099 ith,
1100 id,
1101 v
1102 );
1103 }
1104 }
1105
1106 #[test]
1107 fn vec_progress_matches_reference_model_for_monotonic_updates() {
1108 let cases = [
1109 (vec![btreeset! {0, 1, 2, 3, 4}], vec![5, 6]),
1110 (vec![btreeset! {0, 1, 2}, btreeset! {2, 3, 4}], vec![5, 6]),
1111 ];
1112
1113 for (case_id, (quorum_set, learners)) in cases.into_iter().enumerate() {
1114 for seed in 0..32 {
1115 let mut seed = seed + 1;
1116 let mut progress =
1117 VecProgress::<(u64, u64), _>::new(quorum_set.clone(), learners.clone(), |id| {
1118 (id, 0)
1119 });
1120
1121 assert_matches_model(&progress, &format!("case-{case_id} seed-{seed} initial"));
1122
1123 for step in 0..128 {
1124 let id = next_random(&mut seed) % 8;
1125 let value = progress.get(&id).map(|entry| entry.1).unwrap_or_default()
1126 + next_random(&mut seed) % 7
1127 + 1;
1128 let got = copy_option(progress.update_progress(&id, value));
1129 let want = model_quorum_accepted(&progress.quorum_set, &progress.entries);
1130 let want_result = progress.get(&id).map(|_| want);
1131 let context =
1132 format!("case-{case_id} seed-{seed} step-{step} update-{id}-{value}");
1133
1134 assert_eq!(
1135 want_result, got,
1136 "{context}: entries: {:?}",
1137 progress.entries
1138 );
1139 assert_matches_model(&progress, &context);
1140 }
1141 }
1142 }
1143 }
1144
1145 #[test]
1146 fn vec_progress_matches_reference_model_after_random_quorum_upgrades() {
1147 let quorum_sets = [
1148 vec![btreeset! {0, 1, 2, 3, 4}],
1149 vec![btreeset! {0, 1, 2}, btreeset! {2, 3, 4}],
1150 vec![btreeset! {2, 3, 4}],
1151 vec![btreeset! {1, 3, 5}, btreeset! {3, 4, 5, 6}],
1152 vec![btreeset! {0, 5, 6}],
1153 ];
1154 let known_ids = (0..=8).collect::<Vec<_>>();
1155
1156 for seed in 0..16 {
1157 let mut seed = seed + 11;
1158 let quorum_set = quorum_sets[0].clone();
1159 let learner_ids = learner_ids_for(&quorum_set, known_ids.clone());
1160 let mut progress =
1161 VecProgress::<(u64, u64), _>::new(quorum_set, learner_ids, |id| (id, 0));
1162
1163 assert_matches_model(&progress, &format!("seed-{seed} initial"));
1164
1165 for round in 0..24 {
1166 for step in 0..16 {
1167 let id = next_random(&mut seed) % 10;
1168 let value = progress.get(&id).map(|entry| entry.1).unwrap_or_default()
1169 + next_random(&mut seed) % 11
1170 + 1;
1171 progress.update_progress(&id, value);
1172 assert_matches_model(
1173 &progress,
1174 &format!("seed-{seed} round-{round} step-{step} update"),
1175 );
1176 }
1177
1178 let quorum_index = next_random(&mut seed) as usize % quorum_sets.len();
1179 let quorum_set = quorum_sets[quorum_index].clone();
1180 let learner_ids = learner_ids_for(&quorum_set, known_ids.clone());
1181
1182 progress = progress.upgrade_quorum_set(quorum_set, learner_ids, |id| (id, 0));
1183 assert_matches_model(
1184 &progress,
1185 &format!("seed-{seed} round-{round} upgrade-{quorum_index}"),
1186 );
1187 }
1188 }
1189 }
1190
1191 #[test]
1192 fn vec_progress_joint_quorum_update_progress() {
1193 let quorum_set = vec![btreeset! {0, 1, 2}, btreeset! {2, 3, 4}];
1194 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [5, 6], |id| (id, 0));
1195
1196 let cases = [
1197 (0, 5, 0),
1198 (1, 5, 0),
1199 (2, 4, 0),
1200 (3, 4, 4),
1201 (4, 6, 4),
1202 (3, 6, 5),
1203 (2, 7, 5),
1204 (0, 7, 6),
1205 ];
1206
1207 for (ith, (id, value, want_quorum_accepted)) in cases.iter().enumerate() {
1208 let got = copy_option(progress.update_progress(id, *value));
1209 let context = format!("{ith}-th case: id:{id}, value:{value}");
1210
1211 assert_eq!(
1212 Some(*want_quorum_accepted),
1213 got,
1214 "{context}: entries: {:?}",
1215 progress.entries
1216 );
1217 assert_matches_model(&progress, &context);
1218 }
1219
1220 let entries: Vec<_> = progress.collect_mapped(|item| (item.0, item.1));
1221 assert_eq!(
1222 vec![(2, 7), (0, 7), (4, 6), (3, 6), (1, 5), (5, 0), (6, 0)],
1223 entries
1224 );
1225 }
1226
1227 #[test]
1228 fn vec_progress_non_member_and_learner_edge_cases() {
1229 let quorum_set = vec![btreeset! {0, 1, 2}];
1230 assert!(
1231 std::panic::catch_unwind(|| {
1232 VecProgress::<(u64, u64), _>::new(quorum_set.clone(), [1, 3, 3], |id| (id, 0))
1233 })
1234 .is_err(),
1235 "duplicate IDs are invalid"
1236 );
1237
1238 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [3], |id| (id, 0));
1239
1240 assert_eq!(vec![(0, 0), (1, 0), (2, 0), (3, 0)], progress.entries);
1241 assert_eq!(3, progress.voter_count);
1242 assert_eq!(Some(true), progress.is_voter(&1));
1243 assert_eq!(Some(false), progress.is_voter(&3));
1244 assert_eq!(None, progress.is_voter(&9));
1245
1246 assert_eq!(Some(0), copy_option(progress.update_progress(&3, 7)));
1247 assert_eq!(vec![(0, 0), (1, 0), (2, 0), (3, 7)], progress.entries);
1248
1249 assert_eq!(Some(0), copy_option(progress.update_progress(&1, 5)));
1250 assert_eq!(vec![(1, 5), (0, 0), (2, 0), (3, 7)], progress.entries);
1251
1252 assert_eq!(Some(4), copy_option(progress.update_progress(&2, 4)));
1253 assert_eq!(vec![(1, 5), (2, 4), (0, 0), (3, 7)], progress.entries);
1254
1255 let quorum_set = vec![btreeset! {0, 1, 2}];
1256 let mut no_learners = VecProgress::<(u64, u64), _>::new(quorum_set, [], |id| (id, 0));
1257
1258 assert_eq!(vec![(0, 0), (1, 0), (2, 0)], no_learners.entries);
1259 assert_eq!(3, no_learners.voter_count);
1260 assert_eq!(None, copy_option(no_learners.update_progress(&9, 5)));
1261 assert_eq!(vec![(0, 0), (1, 0), (2, 0)], no_learners.entries);
1262 }
1263
1264 #[test]
1265 fn vec_progress_custom_quorum_set() {
1266 let quorum_set = RequiredSetQuorum::new([0, 1, 2, 3], [0, 3]);
1267 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [], |id| (id, 0));
1268
1269 assert_eq!(Some(0), copy_option(progress.update_progress(&1, 10)));
1270 assert_eq!(Some(0), copy_option(progress.update_progress(&2, 9)));
1271 assert_eq!(Some(0), copy_option(progress.update_progress(&0, 8)));
1272 assert_matches_model(&progress, "custom quorum before required set is reached");
1273
1274 assert_eq!(vec![(1, 10), (2, 9), (0, 8), (3, 0)], progress.entries);
1275
1276 assert_eq!(Some(7), copy_option(progress.update_progress(&3, 7)));
1277 assert_eq!(&7, progress.quorum_accepted());
1278 assert_matches_model(&progress, "custom quorum reaches required set");
1279
1280 assert_eq!(Some(8), copy_option(progress.update_progress(&3, 11)));
1281 assert_eq!(vec![(3, 11), (1, 10), (2, 9), (0, 8)], progress.entries);
1282 assert_matches_model(&progress, "custom quorum follows required set threshold");
1283 }
1284
1285 #[test]
1286 fn vec_progress_update_progress_with() {
1287 let quorum_set = vec![btreeset! {0, 1, 2, 3, 4}];
1288 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [6], |id| (id, 0));
1289
1290 let got = progress.update_progress_with(&1, |x| *x += 2);
1293 assert_eq!(Some(&0), got, "case 0: id:1, +=2");
1294
1295 let got = progress.update_progress_with(&2, |x| *x += 3);
1297 assert_eq!(Some(&0), got, "case 1: id:2, +=3");
1298
1299 let got = progress.update_progress_with(&3, |x| *x = 1);
1301 assert_eq!(Some(&1), got, "case 2: id:3, =1");
1302
1303 let got = progress.update_progress_with(&4, |x| *x += 5);
1305 assert_eq!(Some(&2), got, "case 3: id:4, +5");
1306
1307 let got = progress.update_progress_with(&0, |x| {
1309 *x += 4;
1310 assert_eq!(4, *x, "closure sees the updated value");
1311 });
1312 assert_eq!(Some(&3), got, "case 4: id:0, +=4");
1313
1314 let got = progress.update_progress_with(&3, |x| *x = (*x).max(2));
1316 assert_eq!(Some(&3), got, "case 5: id:3, max(2)");
1317
1318 let got = progress.update_progress_with(&1, |x| *x *= 2);
1320 assert_eq!(Some(&4), got, "case 6: id:1, *=2");
1321
1322 assert_eq!(Some(&(0, 4)), progress.get(&0));
1324 assert_eq!(Some(&(1, 4)), progress.get(&1));
1325 assert_eq!(Some(&(2, 3)), progress.get(&2));
1326 assert_eq!(Some(&(3, 2)), progress.get(&3));
1327 assert_eq!(Some(&(4, 5)), progress.get(&4));
1328 assert_eq!(Some(&(6, 0)), progress.get(&6));
1329
1330 let got = progress.update_progress_with(&9, |x| *x = 10);
1332 assert_eq!(None, got, "nonexistent id returns None");
1333 }
1334
1335 #[test]
1336 fn vec_progress_update_data_with() {
1337 let quorum_set = vec![btreeset! {0, 1, 2}];
1338 let mut progress =
1339 VecProgress::<IdValData<u64, u64, &'static str>, _>::new(quorum_set, [3], |id| {
1340 IdValData::new(id, 0, "foo")
1341 });
1342
1343 assert_eq!(Some(&0), progress.update_progress(&1, 2));
1344
1345 let stats_before = (
1346 progress.stat().update_count,
1347 progress.stat().move_count,
1348 progress.stat().is_quorum_count,
1349 );
1350
1351 assert_eq!(
1352 Some(&"bar"),
1353 progress.update_data_with(&1, |data| *data = "bar")
1354 );
1355 assert_eq!(
1356 None,
1357 progress.update_data_with(&9, |data| *data = "unknown")
1358 );
1359
1360 assert_eq!(
1361 vec![
1362 IdValData::new(1, 2, "bar"),
1363 IdValData::new(0, 0, "foo"),
1364 IdValData::new(2, 0, "foo"),
1365 IdValData::new(3, 0, "foo"),
1366 ],
1367 progress.iter().cloned().collect::<Vec<_>>()
1368 );
1369 assert_eq!(&0, progress.quorum_accepted());
1370 assert_eq!(
1371 stats_before,
1372 (
1373 progress.stat().update_count,
1374 progress.stat().move_count,
1375 progress.stat().is_quorum_count,
1376 )
1377 );
1378 }
1379
1380 #[test]
1381 fn vec_progress_update_does_not_move_learner_elt() {
1382 let quorum_set = vec![btreeset! {0, 1, 2, 3, 4}];
1383 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [6], |id| (id, 0));
1384
1385 assert_eq!(Some(5), progress.index(&6));
1386
1387 progress.update_progress(&6, 6);
1388 assert_eq!(Some(5), progress.index(&6), "learner is not moved");
1389
1390 progress.update_progress(&4, 4);
1391 assert_eq!(Some(0), progress.index(&4), "voter is not moved");
1392 }
1393
1394 #[test]
1395 fn vec_progress_upgrade_quorum_set() {
1396 let qs012 = vec![btreeset! {0, 1, 2}];
1397 let qs012_345 = vec![btreeset! {0, 1, 2}, btreeset! {3, 4, 5}];
1398 let qs345 = vec![btreeset! {3, 4, 5}];
1399
1400 let mut p012 = VecProgress::<(u64, u64), _>::new(qs012, [5], |id| (id, 0));
1403
1404 p012.update_progress(&0, 5);
1405 p012.update_progress(&1, 6);
1406 p012.update_progress(&5, 9);
1407 assert_eq!(&5, p012.quorum_accepted());
1408
1409 let mut p012_345 = p012.upgrade_quorum_set(qs012_345, [6], |id| (id, 0));
1412 assert_eq!(
1413 &0,
1414 p012_345.quorum_accepted(),
1415 "quorum extended from 012 to 012_345, quorum-accepted falls back"
1416 );
1417 assert_eq!(Some(&(5, 9)), p012_345.get(&5), "inherit learner progress");
1418
1419 p012_345.update_progress(&3, 7);
1422 p012_345.update_progress(&4, 8);
1423 assert_eq!(&5, p012_345.quorum_accepted());
1424
1425 let p345 = p012_345.upgrade_quorum_set(qs345, [1], |id| (id, 0));
1426
1427 assert_eq!(
1428 &8,
1429 p345.quorum_accepted(),
1430 "shrink quorum set, greater value becomes quorum-accepted"
1431 );
1432 assert_eq!(Some(&(1, 6)), p345.get(&1), "inherit voter progress");
1433 }
1434
1435 #[test]
1436 fn vec_progress_upgrade_joint_quorum_set() {
1437 let qs01234 = vec![btreeset! {0, 1, 2, 3, 4}];
1438 let qs012_234 = vec![btreeset! {0, 1, 2}, btreeset! {2, 3, 4}];
1439 let qs345 = vec![btreeset! {3, 4, 5}];
1440
1441 let mut p = VecProgress::<(u64, u64), _>::new(qs01234, [5], |id| (id, 0));
1442
1443 for (id, value) in [(0, 9), (1, 8), (2, 7), (3, 2), (4, 1), (5, 10)] {
1444 p.update_progress(&id, value);
1445 }
1446
1447 assert_eq!(&7, p.quorum_accepted());
1448
1449 let mut joint = p.upgrade_quorum_set(qs012_234, [5, 6], |id| (id, 0));
1450
1451 assert_eq!(
1452 &2,
1453 joint.quorum_accepted(),
1454 "joint quorum lowers the accepted value"
1455 );
1456 let entries: Vec<_> = joint.collect_mapped(|item| (item.0, item.1));
1457 assert_eq!(
1458 vec![(0, 9), (1, 8), (2, 7), (3, 2), (4, 1), (5, 10), (6, 0)],
1459 entries
1460 );
1461 assert_matches_model(&joint, "after upgrade to joint quorum");
1462
1463 joint.update_progress(&3, 8);
1464 joint.update_progress(&4, 8);
1465 assert_eq!(&8, joint.quorum_accepted());
1466 assert_matches_model(&joint, "after joint quorum catches up");
1467
1468 let shrunk = joint.upgrade_quorum_set(qs345, [0], |id| (id, 0));
1469
1470 assert_eq!(&8, shrunk.quorum_accepted());
1471 let entries: Vec<_> = shrunk.collect_mapped(|item| (item.0, item.1));
1472 assert_eq!(vec![(5, 10), (3, 8), (4, 8), (0, 9)], entries);
1473 assert_matches_model(&shrunk, "after shrinking joint quorum");
1474 }
1475
1476 #[test]
1477 fn vec_progress_is_voter() {
1478 let quorum_set = vec![btreeset! {0, 1, 2, 3, 4}];
1479 let progress = VecProgress::<(u64, u64), _>::new(quorum_set, [6, 7], |id| (id, 0));
1480
1481 assert_eq!(Some(true), progress.is_voter(&1));
1482 assert_eq!(Some(true), progress.is_voter(&3));
1483 assert_eq!(Some(false), progress.is_voter(&7));
1484 assert_eq!(None, progress.is_voter(&8));
1485 }
1486
1487 #[test]
1488 fn vec_progress_display() {
1489 let quorum_set = vec![btreeset! {0, 1, 2}];
1490 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [3], |id| (id, 0));
1491
1492 progress.update_progress(&1, 5);
1493 progress.update_progress(&2, 3);
1494
1495 let display = format!(
1496 "{}",
1497 progress.display_with(|f, item| write!(f, "{}: {}", item.0, item.1))
1498 );
1499 assert_eq!("{1: 5, 2: 3, 0: 0, 3: 0}", display);
1500 }
1501
1502 #[test]
1503 fn vec_progress_iter_mut_without_reorder() {
1504 let quorum_set = vec![btreeset! {0, 1, 2}];
1505 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [3], |id| (id, 0));
1506
1507 for item in progress.iter_mut_without_reorder() {
1509 if item.0 == 1 {
1510 item.1 = 10;
1511 }
1512 }
1513
1514 assert_eq!(Some(&(1, 10)), progress.get(&1));
1515 assert_eq!(Some(&(0, 0)), progress.get(&0));
1516 assert_eq!(Some(&(2, 0)), progress.get(&2));
1517 }
1518
1519 #[test]
1520 fn vec_progress_stat() {
1521 let quorum_set = vec![btreeset! {0, 1, 2}];
1522 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [3], |id| (id, 0));
1523
1524 assert_eq!(
1525 (0, 0, 0),
1526 (
1527 progress.stat().update_count,
1528 progress.stat().move_count,
1529 progress.stat().is_quorum_count,
1530 )
1531 );
1532
1533 progress.update_progress(&3, 10);
1534 assert_eq!(
1535 (1, 0, 0),
1536 (
1537 progress.stat().update_count,
1538 progress.stat().move_count,
1539 progress.stat().is_quorum_count,
1540 )
1541 );
1542
1543 progress.update_progress(&1, 5);
1544 assert_eq!(
1545 (2, 1, 1),
1546 (
1547 progress.stat().update_count,
1548 progress.stat().move_count,
1549 progress.stat().is_quorum_count,
1550 )
1551 );
1552
1553 progress.update_progress(&2, 4);
1554 assert_eq!(
1555 (3, 2, 2),
1556 (
1557 progress.stat().update_count,
1558 progress.stat().move_count,
1559 progress.stat().is_quorum_count,
1560 )
1561 );
1562
1563 progress.update_progress(&1, 6);
1564 assert_eq!(
1565 (4, 3, 2),
1566 (
1567 progress.stat().update_count,
1568 progress.stat().move_count,
1569 progress.stat().is_quorum_count,
1570 )
1571 );
1572
1573 progress.update_progress(&9, 7);
1574 assert_eq!(
1575 (5, 3, 2),
1576 (
1577 progress.stat().update_count,
1578 progress.stat().move_count,
1579 progress.stat().is_quorum_count,
1580 )
1581 );
1582 }
1583
1584 #[test]
1585 fn vec_progress_display_with() {
1586 let quorum_set = vec![btreeset! {0, 1, 2}];
1587 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [3], |id| (id, 0));
1588
1589 progress.update_progress(&1, 5);
1590 progress.update_progress(&2, 3);
1591
1592 let display = progress.display_with(|f, item| write!(f, "{}={}", item.0, item.1));
1593
1594 let output = format!("{}", display);
1595 assert_eq!("{1=5, 2=3, 0=0, 3=0}", output);
1596 }
1597
1598 #[test]
1599 fn vec_progress_increase_to() {
1600 let quorum_set = vec![btreeset! {0, 1, 2, 3, 4}];
1601 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [6], |id| (id, 0));
1602
1603 progress.increase_to(&1, 5);
1605 assert_eq!(Some(&(1, 5)), progress.get(&1));
1606
1607 progress.increase_to(&1, 3);
1609 assert_eq!(Some(&(1, 5)), progress.get(&1));
1610
1611 progress.increase_to(&1, 7);
1613 assert_eq!(Some(&(1, 7)), progress.get(&1));
1614
1615 let result = progress.increase_to(&9, 10);
1617 assert!(result.is_none());
1618 }
1619
1620 #[test]
1621 fn vec_progress_collect_mapped() {
1622 let quorum_set = vec![btreeset! {0, 1, 2}];
1623 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [3], |id| (id, 0));
1624
1625 progress.update_progress(&1, 5);
1626 progress.update_progress(&2, 3);
1627
1628 let ids: Vec<u64> = progress.collect_mapped(|item| item.0);
1630 assert_eq!(vec![1, 2, 0, 3], ids);
1631
1632 let values: Vec<u64> = progress.collect_mapped(|item| item.1);
1634 assert_eq!(vec![5, 3, 0, 0], values);
1635
1636 let pairs: Vec<(u64, u64)> = progress.collect_mapped(|item| (item.0, item.1));
1638 assert_eq!(vec![(1, 5), (2, 3), (0, 0), (3, 0)], pairs);
1639 }
1640
1641 #[test]
1642 fn vec_progress_reset_entry_with() {
1643 let quorum_set = vec![btreeset! {0, 1, 2, 3, 4, 5, 6}];
1645 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [7], |id| (id, 0));
1646
1647 progress.update_progress(&0, 12);
1648 progress.update_progress(&1, 11);
1649 progress.update_progress(&2, 10);
1650 progress.update_progress(&3, 9);
1651 assert_eq!(&9, progress.quorum_accepted());
1652
1653 let entry = progress.reset_entry_with(&1, |entry| entry.1 = 0);
1656 assert_eq!(Some(&(1, 0)), entry);
1657 assert_eq!(
1658 &9,
1659 progress.quorum_accepted(),
1660 "reset never lowers quorum-accepted"
1661 );
1662 assert_eq!(
1663 vec![
1664 (0, 12),
1665 (2, 10),
1666 (3, 9),
1667 (1, 0),
1668 (4, 0),
1669 (5, 0),
1670 (6, 0),
1671 (7, 0)
1672 ],
1673 progress.entries
1674 );
1675 assert_voter_prefix_is_sorted(&progress, "after reset");
1676
1677 assert_eq!(Some(9), copy_option(progress.update_progress(&4, 10)));
1680 assert_matches_model(&progress, "after catching up to 10");
1681
1682 assert_eq!(Some(10), copy_option(progress.update_progress(&5, 10)));
1684 assert_matches_model(&progress, "after a real quorum at 10");
1685
1686 assert_eq!(
1688 Some(&(7, 0)),
1689 progress.reset_entry_with(&7, |entry| entry.1 = 0)
1690 );
1691 assert_eq!(None, progress.reset_entry_with(&9, |entry| entry.1 = 0));
1692 }
1693
1694 #[test]
1695 fn vec_progress_matches_reference_model_with_resets() {
1696 let cases = [
1697 (vec![btreeset! {0, 1, 2, 3, 4, 5, 6}], vec![7]),
1698 (vec![btreeset! {0, 1, 2}, btreeset! {2, 3, 4}], vec![5, 6]),
1699 ];
1700
1701 for (case_id, (quorum_set, learners)) in cases.into_iter().enumerate() {
1702 for seed in 0..32 {
1703 let mut seed = seed + 3;
1704 let mut progress =
1705 VecProgress::<(u64, u64), _>::new(quorum_set.clone(), learners.clone(), |id| {
1706 (id, 0)
1707 });
1708
1709 let mut want = 0;
1712
1713 for step in 0..128 {
1714 let id = (next_random(&mut seed) >> 32) % 8;
1717 let context = format!("case-{case_id} seed-{seed} step-{step} id-{id}");
1718
1719 if (next_random(&mut seed) >> 32).is_multiple_of(8) {
1720 progress.reset_entry_with(&id, |entry| entry.1 = 0);
1721 } else {
1722 let value = progress.get(&id).map(|entry| entry.1).unwrap_or_default()
1723 + next_random(&mut seed) % 7
1724 + 1;
1725 progress.update_progress(&id, value);
1726 }
1727
1728 want = want.max(model_quorum_accepted(
1729 &progress.quorum_set,
1730 &progress.entries,
1731 ));
1732 assert_eq!(
1733 &want,
1734 progress.quorum_accepted(),
1735 "{context}: entries: {:?}",
1736 progress.entries
1737 );
1738 assert_voter_prefix_is_sorted(&progress, &context);
1739 }
1740 }
1741 }
1742 }
1743
1744 #[test]
1745 fn vec_progress_sub_quorum_commit_regression() {
1746 let quorum_set = vec![btreeset! {0, 1, 2, 3, 4}];
1747 let mut progress = VecProgress::<(u64, u64), _>::new(quorum_set, [], |id| (id, 0));
1748
1749 progress.update_progress(&0, 5); progress.update_progress(&1, 3); progress.update_progress(&2, 4); progress.update_progress(&2, 10); let qa = progress.update_progress(&3, 6);
1756
1757 assert_eq!(Some(&5), qa);
1761 }
1762}