You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
103 lines
2.7 KiB
103 lines
2.7 KiB
//! Failure counting/capping
|
|
use super::*;
|
|
use std::{
|
|
fmt, error,
|
|
};
|
|
|
|
/// A measure of failures, used to track or to check failures.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord, Copy)]
|
|
pub struct Failures
|
|
{
|
|
/// Number of failures happened back-to-back.
|
|
pub seq: usize,
|
|
/// Total number of failures over the socket's lifetime.
|
|
///
|
|
/// Set allowed to `0` for unlimited.
|
|
pub total: usize,
|
|
|
|
/// Window of time to keep failures.
|
|
pub window: Duration,
|
|
/// Number of failures happened in the last `window` of time.
|
|
pub last_window: usize,
|
|
}
|
|
|
|
/// When a failure cap is exceeded, which one is exceeded; and what is the limit that is exceeded?
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum FailureCapExceeded
|
|
{
|
|
/// Too many sequential errors
|
|
Sequential(usize),
|
|
/// Too many total errors
|
|
Total(usize),
|
|
/// Too many errors in the refresh window
|
|
Windowed(usize, Duration),
|
|
}
|
|
|
|
impl error::Error for FailureCapExceeded{}
|
|
impl fmt::Display for FailureCapExceeded
|
|
{
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
match self {
|
|
Self::Sequential(_) => write!(f, "too many sequential errors"),
|
|
Self::Total(_) => write!(f, "too many total errors"),
|
|
Self::Windowed(_, _) => write!(f, "too many errors in refresh window"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FailureCapExceeded
|
|
{
|
|
/// Convert into a detailed report. (This shouldn't be shared with peer, probably.)
|
|
pub fn into_detailed_report(self) -> eyre::Report
|
|
{
|
|
let rep = eyre::Report::from(&self);
|
|
match self {
|
|
|
|
Self::Sequential(cap) => rep.with_section(|| cap.header("Exceeded limit")),
|
|
Self::Total(cap) => rep.with_section(|| cap.header("Exceeded limit")),
|
|
Self::Windowed(cap, w) => rep.with_section(|| cap.header("Exceeded limit"))
|
|
.with_section(|| format!("{:?}", w).header("Refresh window was"))
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Failures
|
|
{
|
|
/// Default allowed failure parameters.
|
|
pub const DEFAULT_ALLOWED: Self = Self {
|
|
seq: 10,
|
|
total: 65536,
|
|
window: Duration::from_secs(10),
|
|
last_window: 5,
|
|
};
|
|
|
|
/// Has this `Failures` exceeded failure cap `other`?
|
|
pub fn cap_check(&self, other: &Self) -> Result<(), FailureCapExceeded>
|
|
{
|
|
macro_rules! chk {
|
|
($name:ident, $err:expr) => {
|
|
if other.$name != 0 && (self.$name >= other.$name) {
|
|
return Err($err)?;
|
|
}
|
|
}
|
|
}
|
|
chk!(seq, FailureCapExceeded::Sequential(other.seq));
|
|
chk!(total, FailureCapExceeded::Total(other.total));
|
|
//debug_assert!(other.window == self.window); //TODO: Should we disallow this?
|
|
chk!(last_window, FailureCapExceeded::Windowed(other.last_window, self.window));
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Default for Failures
|
|
{
|
|
#[inline]
|
|
fn default() -> Self
|
|
{
|
|
Self::DEFAULT_ALLOWED
|
|
}
|
|
}
|
|
|