use std::ops::{Deref, DerefMut};
use std::time::Instant;
use crate::Connection;
use super::core::PooledConnection;
use super::Pool;
#[cfg(feature = "tracing")]
use super::TARGET_POOL;
pub(crate) struct AcquiredConnection {
pub(crate) connection: Connection,
pub(crate) created_at: Instant,
}
#[non_exhaustive]
pub struct PoolGuard<'a> {
pool: &'a Pool,
acquired: Option<AcquiredConnection>,
}
impl<'a> PoolGuard<'a> {
pub(crate) fn new(pool: &'a Pool, pooled: PooledConnection) -> Self {
PoolGuard {
pool,
acquired: Some(AcquiredConnection {
connection: pooled.connection,
created_at: pooled.created_at,
}),
}
}
pub(crate) fn new_fresh(pool: &'a Pool, connection: Connection) -> Self {
PoolGuard {
pool,
acquired: Some(AcquiredConnection {
connection,
created_at: Instant::now(),
}),
}
}
pub fn conn(&mut self) -> &mut Connection {
&mut self
.acquired
.as_mut()
.expect("PoolGuard already released")
.connection
}
pub async fn release(mut self) {
if let Some(acquired) = self.acquired.take() {
#[cfg(feature = "tracing")]
tracing::debug!(target: TARGET_POOL, "Releasing connection back to pool");
self.pool.release_with_metadata(acquired).await;
}
}
pub fn detach(mut self) -> Connection {
let acquired = self.acquired.take().expect("PoolGuard already released");
let mut inner = self.pool.inner.lock();
inner.active_count = inner.active_count.saturating_sub(1);
drop(inner);
self.pool.notify_waiters();
#[cfg(feature = "tracing")]
tracing::debug!(target: TARGET_POOL, "Detaching connection from pool");
acquired.connection
}
pub fn is_active(&self) -> bool {
self.acquired.is_some()
}
}
impl<'a> Deref for PoolGuard<'a> {
type Target = Connection;
fn deref(&self) -> &Connection {
&self
.acquired
.as_ref()
.expect("PoolGuard already released")
.connection
}
}
impl<'a> DerefMut for PoolGuard<'a> {
fn deref_mut(&mut self) -> &mut Connection {
&mut self
.acquired
.as_mut()
.expect("PoolGuard already released")
.connection
}
}
impl<'a> Drop for PoolGuard<'a> {
fn drop(&mut self) {
if let Some(acquired) = self.acquired.take() {
let mut inner = self.pool.inner.lock();
inner.active_count = inner.active_count.saturating_sub(1);
if !acquired.connection.is_idle() {
#[cfg(feature = "tracing")]
tracing::warn!(
target: TARGET_POOL,
"PoolGuard dropped with dirty connection (not idle). \
Connection discarded. Prefer guard.release().await for proper cleanup."
);
drop(inner);
self.pool.notify_waiters();
drop(acquired);
return;
}
if inner.closed {
drop(inner);
self.pool.notify_waiters();
drop(acquired);
return;
}
let now = Instant::now();
inner.idle.push_back(PooledConnection {
connection: acquired.connection,
created_at: acquired.created_at,
last_used_at: now,
acquire_count: 0,
});
#[cfg(feature = "tracing")]
tracing::warn!(
target: TARGET_POOL,
"PoolGuard dropped without calling release().await. \
Connection returned to pool but may need cleanup on next acquire. \
Prefer guard.release().await for proper cleanup."
);
drop(inner);
self.pool.notify_waiters();
}
}
}