#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SourceRemovalCause {
CapacityPressure,
Idle,
CapacityReduced,
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SourceRemoval<K> {
pub source: K,
pub cause: SourceRemovalCause,
}
pub type SourceRemovalReporterError = Box<dyn std::error::Error + Send + Sync>;
#[non_exhaustive]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct SourceRemovalMetrics {
pub capacity_pressure: u64,
pub idle: u64,
pub capacity_reduced: u64,
pub reporter_failures: u64,
}
impl SourceRemovalMetrics {
fn record_removal(&mut self, cause: SourceRemovalCause) {
match cause {
SourceRemovalCause::CapacityPressure => {
self.capacity_pressure = self.capacity_pressure.saturating_add(1);
}
SourceRemovalCause::Idle => {
self.idle = self.idle.saturating_add(1);
}
SourceRemovalCause::CapacityReduced => {
self.capacity_reduced = self.capacity_reduced.saturating_add(1);
}
}
}
fn record_reporter_failure(&mut self) {
self.reporter_failures = self.reporter_failures.saturating_add(1);
}
}
pub(super) fn ignore_source_removal<K>(
_removal: &SourceRemoval<K>,
) -> Result<(), SourceRemovalReporterError> {
Ok(())
}
pub(super) fn report_source_removal<K, F>(
source: K,
cause: SourceRemovalCause,
metrics: &mut SourceRemovalMetrics,
reporter: &mut F,
) where
F: FnMut(&SourceRemoval<K>) -> Result<(), SourceRemovalReporterError>,
{
metrics.record_removal(cause);
let removal = SourceRemoval { source, cause };
let delivered =
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| reporter(&removal).is_ok()))
.unwrap_or(false);
if !delivered {
metrics.record_reporter_failure();
}
}