use std::cmp;
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum ProgressState {
Probe,
Replicate,
Snapshot,
}
impl Default for ProgressState {
fn default() -> ProgressState {
ProgressState::Probe
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Progress {
pub matched: u64,
pub next: u64,
pub state: ProgressState,
pub paused: bool,
pub pending_snapshot: u64,
pub recent_active: bool,
pub ins: Inflights,
pub is_learner: bool,
}
impl Progress {
pub fn new(next: u64, ins_size: usize, is_learner: bool) -> Progress {
Progress {
next,
is_learner,
ins: Inflights::new(ins_size),
..Default::default()
}
}
pub fn reset_state(&mut self, state: ProgressState) {
self.paused = false;
self.pending_snapshot = 0;
self.state = state;
self.ins.reset();
}
pub fn become_probe(&mut self) {
if self.state == ProgressState::Snapshot {
let pending_snapshot = self.pending_snapshot;
self.reset_state(ProgressState::Probe);
self.next = cmp::max(self.matched + 1, pending_snapshot + 1);
} else {
self.reset_state(ProgressState::Probe);
self.next = self.matched + 1;
}
}
pub fn become_replicate(&mut self) {
self.reset_state(ProgressState::Replicate);
self.next = self.matched + 1;
}
pub fn become_snapshot(&mut self, index: u64) {
self.reset_state(ProgressState::Snapshot);
self.pending_snapshot = index;
}
pub(crate) fn resume(&mut self) {
self.paused = false;
}
pub fn pause(&mut self) {
self.paused = true;
}
pub fn maybe_update(&mut self, n: u64) -> bool {
let mut updated = false;
if self.matched < n {
self.matched = n;
updated = true;
self.resume();
}
if self.next < n + 1 {
self.next = n + 1;
}
updated
}
pub fn optimistic_update(&mut self, n: u64) {
self.next = n + 1;
}
pub fn is_paused(&self) -> bool {
match self.state {
ProgressState::Probe => self.paused,
ProgressState::Replicate => self.ins.full(),
ProgressState::Snapshot => true,
}
}
pub fn snapshot_failure(&mut self) {
self.pending_snapshot = 0;
}
pub fn need_snapshot_abort(&mut self) -> bool {
self.state == ProgressState::Snapshot && self.matched >= self.pending_snapshot
}
pub fn maybe_decr_to(&mut self, rejected: u64, last: u64) -> bool {
if self.state == ProgressState::Replicate {
if rejected <= self.matched {
return false;
}
self.next = self.matched + 1;
return true;
}
if self.next == 0 || self.next - 1 != rejected {
return false;
}
self.next = cmp::min(rejected, last + 1);
if self.next < 1 {
self.next = 1;
}
self.resume();
true
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Inflights {
pub start: usize,
pub count: usize,
pub buffer: Vec<u64>,
}
impl Inflights {
pub fn new(cap: usize) -> Inflights {
Inflights {
buffer: Vec::with_capacity(cap),
..Default::default()
}
}
fn reset(&mut self) {
self.start = 0;
self.count = 0;
}
pub fn full(&self) -> bool {
self.count == self.cap()
}
fn cap(&self) -> usize {
self.buffer.capacity()
}
pub(crate) fn add(&mut self, inflight: u64) {
if self.full() {
panic!("cannot add into a full inflights");
}
let mut next = self.start + self.count;
if next >= self.cap() {
next -= self.cap();
}
if next == self.buffer.len() {
self.buffer.push(inflight);
} else {
self.buffer[next] = inflight;
}
self.count += 1;
}
pub(crate) fn free_first_one(&mut self) {
let to = self.buffer[self.start];
self.free_to(to);
}
pub(crate) fn free_to(&mut self, to: u64) {
if self.count == 0 || to < self.buffer[self.start] {
return;
}
let mut i: usize = 0;
let mut idx = self.start;
while i < self.count {
if to < self.buffer[idx] {
break;
}
let size = self.cap();
idx += 1;
if idx >= size {
idx -= size;
}
i += 1;
}
self.count -= i;
self.start = idx;
if self.count == 0 {
self.start = 0;
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_inflight_add() {
let mut inflight = Inflights::new(10);
for i in 0..5 {
inflight.add(i);
}
let wantin = Inflights {
start: 0,
count: 5,
buffer: vec![0, 1, 2, 3, 4],
};
assert_eq!(inflight, wantin);
for i in 5..10 {
inflight.add(i);
}
let wantin2 = Inflights {
start: 0,
count: 10,
buffer: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
};
assert_eq!(inflight, wantin2);
let mut inflight2 = Inflights {
start: 5,
buffer: Vec::with_capacity(10),
..Default::default()
};
inflight2.buffer.extend_from_slice(&vec![0, 0, 0, 0, 0]);
for i in 0..5 {
inflight2.add(i);
}
let wantin21 = Inflights {
start: 5,
count: 5,
buffer: vec![0, 0, 0, 0, 0, 0, 1, 2, 3, 4],
};
assert_eq!(inflight2, wantin21);
for i in 5..10 {
inflight2.add(i);
}
let wantin22 = Inflights {
start: 5,
count: 10,
buffer: vec![5, 6, 7, 8, 9, 0, 1, 2, 3, 4],
};
assert_eq!(inflight2, wantin22);
}
#[test]
fn test_inflight_free_to() {
let mut inflight = Inflights::new(10);
for i in 0..10 {
inflight.add(i);
}
inflight.free_to(4);
let wantin = Inflights {
start: 5,
count: 5,
buffer: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
};
assert_eq!(inflight, wantin);
inflight.free_to(8);
let wantin2 = Inflights {
start: 9,
count: 1,
buffer: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
};
assert_eq!(inflight, wantin2);
for i in 10..15 {
inflight.add(i);
}
inflight.free_to(12);
let wantin3 = Inflights {
start: 3,
count: 2,
buffer: vec![10, 11, 12, 13, 14, 5, 6, 7, 8, 9],
};
assert_eq!(inflight, wantin3);
inflight.free_to(14);
let wantin4 = Inflights {
start: 0,
count: 0,
buffer: vec![10, 11, 12, 13, 14, 5, 6, 7, 8, 9],
};
assert_eq!(inflight, wantin4);
}
#[test]
fn test_inflight_free_first_one() {
let mut inflight = Inflights::new(10);
for i in 0..10 {
inflight.add(i);
}
inflight.free_first_one();
let wantin = Inflights {
start: 1,
count: 9,
buffer: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
};
assert_eq!(inflight, wantin);
}
}