-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: iGxnon <[email protected]>
- Loading branch information
Showing
6 changed files
with
193 additions
and
3 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -190,3 +190,6 @@ mod rpc; | |
|
||
/// Snapshot | ||
mod snapshot; | ||
|
||
/// Command tracker shared between client and server | ||
mod tracker; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
#![allow(unused)] // TODO remove | ||
|
||
use bitvec::vec::BitVec; | ||
use std::ops::{AddAssign, Sub}; | ||
|
||
/// Track sequence number for commands | ||
#[derive(Debug, Default)] | ||
pub(super) struct Tracker { | ||
/// First incomplete seq num, it will be advanced by client | ||
first_incomplete: u64, | ||
/// inflight seq nums proposed by the client, each bit | ||
/// represent the received status starting from `first_incomplete` | ||
inflight: BitVec, | ||
} | ||
|
||
impl Tracker { | ||
/// Record a sequence number, return whether it is duplicated | ||
#[allow(clippy::as_conversions)] | ||
#[allow(clippy::cast_possible_truncation)] // TODO: support 32 bits computers? | ||
#[allow(clippy::indexing_slicing)] // it is checked | ||
pub(crate) fn record(&mut self, seq_num: u64) -> bool { | ||
if seq_num < self.first_incomplete { | ||
return true; | ||
} | ||
let gap = seq_num.sub(self.first_incomplete) as usize; | ||
if gap == 0 { | ||
// received the next sequence number, advanced the first_incomplete | ||
// and pop the front of inflight | ||
self.first_incomplete.add_assign(1); | ||
if !self.inflight.is_empty() { | ||
let _ig = self.inflight.remove(0); | ||
} | ||
} else if gap < self.inflight.len() { | ||
// received the sequence number that is recorded in inflight | ||
// check its status to determine whether it is duplicated | ||
if self.inflight[gap] { | ||
return true; | ||
} | ||
// mark it is received | ||
self.inflight.set(gap, true); | ||
} else { | ||
// received the sequence number that exceed inflight, extend | ||
// the inflight and record the inflight[gap] as received | ||
// TODO: set an upper bound of gaps? | ||
self.inflight | ||
.extend(std::iter::repeat(false).take(gap.sub(self.inflight.len()))); | ||
self.inflight.push(true); | ||
} | ||
loop { | ||
if self.inflight.is_empty() || !self.inflight[0] { | ||
break; | ||
} | ||
let _ig = self.inflight.remove(0); | ||
self.first_incomplete.add_assign(1); | ||
} | ||
false | ||
} | ||
|
||
/// Reset the tracker | ||
pub(crate) fn reset(&mut self) { | ||
self.first_incomplete = 0; | ||
self.inflight.clear(); | ||
} | ||
|
||
/// Get the first incomplete sequence number | ||
pub(crate) fn first_incomplete(&self) -> u64 { | ||
self.first_incomplete | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_check_not_duplicate_ordered() { | ||
let mut tracker = Tracker::default(); | ||
for i in 0..1024 { | ||
assert!(!tracker.record(i)); | ||
assert_eq!(tracker.first_incomplete, i + 1); | ||
assert!(tracker.inflight.len() <= 1); | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_check_duplicate_ordered() { | ||
let mut tracker = Tracker::default(); | ||
for i in 0..512 { | ||
assert!(!tracker.record(i)); | ||
assert_eq!(tracker.first_incomplete, i + 1); | ||
assert!(tracker.inflight.len() <= 1); | ||
} | ||
for i in 0..512 { | ||
assert!(tracker.record(i)); | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_check_duplicate_gap() { | ||
let mut tracker = Tracker::default(); | ||
assert!(!tracker.record(0)); | ||
assert!(!tracker.record(1)); | ||
assert!(!tracker.record(1000)); | ||
assert!(!tracker.record(1001)); | ||
|
||
assert!(tracker.record(0)); | ||
assert!(tracker.record(1)); | ||
assert!(tracker.record(1000)); | ||
assert!(tracker.record(1001)); | ||
assert_eq!(tracker.first_incomplete, 2); | ||
} | ||
|
||
#[test] | ||
fn test_check_duplicate_clear_inflight() { | ||
let mut tracker = Tracker::default(); | ||
for i in (1..256).step_by(2) { | ||
assert!(!tracker.record(i)); | ||
} | ||
for i in (0..256).step_by(2) { | ||
assert!(!tracker.record(i)); | ||
} | ||
assert_eq!(tracker.inflight.len(), 0); | ||
} | ||
} |