use std::error::Error;
use std::panic::{AssertUnwindSafe, catch_unwind};
use std::sync::Arc;
use crate::core::connection::{CloseReason, PooledConnection};
use crate::resilience::breaker::BreakerState;
pub trait PoolListener<T>: Send + Sync {
fn on_connection_created(&self, _conn: &PooledConnection<T>, _duration: f64) {}
fn on_connection_closed(&self, _conn: &PooledConnection<T>, _reason: CloseReason) {}
fn on_create_failed(&self, _error: &(dyn Error + 'static)) {}
fn on_acquired(&self, _conn: &PooledConnection<T>, _wait_time: f64) {}
fn on_released(&self, _conn: &PooledConnection<T>, _hold_time: f64) {}
fn on_acquire_timeout(&self, _waited: f64) {}
fn on_connection_leak(&self, _conn: &PooledConnection<T>, _held_for: f64) {}
fn on_breaker_state_change(&self, _old: BreakerState, _new: BreakerState) {}
}
pub(crate) struct Listeners<T> {
listeners: Vec<Arc<dyn PoolListener<T>>>,
}
impl<T> Listeners<T> {
pub(crate) fn new(listeners: Vec<Arc<dyn PoolListener<T>>>) -> Self {
Self { listeners }
}
pub(crate) fn safe<F>(&self, f: F)
where
F: Fn(&dyn PoolListener<T>),
{
for listener in &self.listeners {
let l = listener.as_ref();
if catch_unwind(AssertUnwindSafe(|| f(l))).is_err() {
eprintln!("connpool: a pool listener panicked (ignored)");
}
}
}
pub(crate) fn breaker_change(&self, old: BreakerState, new: BreakerState) {
self.safe(|l| l.on_breaker_state_change(old, new));
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicU32, Ordering};
struct Counting(Arc<AtomicU32>);
impl PoolListener<u32> for Counting {
fn on_acquire_timeout(&self, _waited: f64) {
self.0.fetch_add(1, Ordering::SeqCst);
}
}
struct Panicky;
impl PoolListener<u32> for Panicky {
fn on_acquire_timeout(&self, _waited: f64) {
panic!("boom");
}
}
#[test]
fn default_hooks_are_noops() {
struct Noop;
impl PoolListener<u32> for Noop {}
let n = Noop;
let pc = PooledConnection::new(1u32, 0.0, None);
n.on_connection_created(&pc, 1.0);
n.on_connection_closed(&pc, CloseReason::Idle);
let e: Box<dyn Error + Send + Sync> = "x".into();
n.on_create_failed(&*e);
n.on_acquired(&pc, 1.0);
n.on_released(&pc, 1.0);
n.on_acquire_timeout(1.0);
n.on_connection_leak(&pc, 1.0);
n.on_breaker_state_change(BreakerState::Closed, BreakerState::Open);
}
#[test]
fn dispatch_isolates_panics_and_reaches_all() {
let count = Arc::new(AtomicU32::new(0));
let group: Listeners<u32> =
Listeners::new(vec![Arc::new(Panicky), Arc::new(Counting(count.clone()))]);
group.safe(|l| l.on_acquire_timeout(2.0));
assert_eq!(count.load(Ordering::SeqCst), 1);
}
}