use std::{sync::Arc, time::Duration};
use parking_lot::RwLock;
#[derive(Debug)]
pub(crate) struct Inner {
max: isize,
score: isize,
}
#[derive(Debug, Clone)]
pub(crate) struct Awareness {
pub(crate) inner: Arc<RwLock<Inner>>,
#[cfg(feature = "metrics")]
pub(crate) metric_labels: Arc<Vec<metrics::Label>>,
}
impl Awareness {
pub(crate) fn new(
max: isize,
#[cfg(feature = "metrics")] metric_labels: Arc<Vec<metrics::Label>>,
) -> Self {
Self {
inner: Arc::new(RwLock::new(Inner { max, score: 0 })),
#[cfg(feature = "metrics")]
metric_labels,
}
}
pub(crate) fn apply_delta(&self, delta: isize) {
let (_initial, _fnl) = {
let mut inner = self.inner.write();
let initial = inner.score;
inner.score += delta;
if inner.score < 0 {
inner.score = 0;
} else if inner.score > inner.max - 1 {
inner.score = inner.max - 1;
}
(initial, inner.score)
};
#[cfg(feature = "metrics")]
{
if _initial != _fnl {
metrics::gauge!("memberlist.health.score", self.metric_labels.iter()).set(_fnl as f64);
}
}
}
pub(crate) fn get_health_score(&self) -> isize {
self.inner.read().score
}
pub(crate) fn scale_timeout(&self, timeout: Duration) -> Duration {
let score = self.inner.read().score;
timeout * ((score + 1) as u32)
}
}
#[cfg(test)]
#[test]
fn test_awareness() {
let cases = vec![
(0, 0, Duration::from_secs(1)),
(-1, 0, Duration::from_secs(1)),
(-10, 0, Duration::from_secs(1)),
(1, 1, Duration::from_secs(2)),
(-1, 0, Duration::from_secs(1)),
(10, 7, Duration::from_secs(8)),
(-1, 6, Duration::from_secs(7)),
(-1, 5, Duration::from_secs(6)),
(-1, 4, Duration::from_secs(5)),
(-1, 3, Duration::from_secs(4)),
(-1, 2, Duration::from_secs(3)),
(-1, 1, Duration::from_secs(2)),
(-1, 0, Duration::from_secs(1)),
(-1, 0, Duration::from_secs(1)),
];
let a = Awareness::new(
8,
#[cfg(feature = "metrics")]
Arc::new(vec![]),
);
for (delta, score, timeout) in cases {
a.apply_delta(delta);
assert_eq!(a.get_health_score(), score);
assert_eq!(a.scale_timeout(Duration::from_secs(1)), timeout);
}
}