use std::sync::{Arc, Mutex};
#[derive(Clone, Debug, Default)]
pub struct SinkFailures(Arc<Mutex<Vec<(String, String)>>>);
impl SinkFailures {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub(crate) fn record(&self, sink: &str, reason: String) {
let mut entries = self.0.lock().expect("sink failures lock");
match entries.iter_mut().find(|(name, _)| name == sink) {
Some(entry) => entry.1 = reason,
None => entries.push((sink.to_owned(), reason)),
}
}
pub(crate) fn describe(&self) -> Option<String> {
let entries = self.0.lock().expect("sink failures lock");
(!entries.is_empty()).then(|| {
entries
.iter()
.map(|(sink, reason)| format!("sink `{sink}` abandoned a batch: {reason}"))
.collect::<Vec<_>>()
.join("; ")
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn describe_keeps_the_latest_reason_per_sink() {
let failures = SinkFailures::new();
assert_eq!(failures.describe(), None);
failures.record("a", "first".into());
failures.record("b", "other".into());
failures.record("a", "second".into());
assert_eq!(
failures.describe().as_deref(),
Some("sink `a` abandoned a batch: second; sink `b` abandoned a batch: other")
);
}
}