use std::{
cell::{Cell, RefCell},
fmt,
future::Future,
ops::{Deref, DerefMut},
pin::Pin,
rc::Rc,
sync::{
Arc, Mutex, PoisonError,
atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering::Relaxed},
},
task::{Context, Poll, Waker},
time::{Duration, Instant},
};
use crate::{
builder::{Builder, PoolConfig},
manage::{ManageConnection, RunError},
};
pub struct Pool<M: ManageConnection> {
shared: Arc<Shared<M>>,
}
pub(crate) struct Shared<M: ManageConnection> {
manager: M,
config: PoolConfig,
closed: AtomicBool,
metrics: Mutex<Vec<Arc<Metrics>>>,
}
impl<M: ManageConnection> Clone for Pool<M> {
fn clone(&self) -> Self {
Self {
shared: self.shared.clone(),
}
}
}
impl<M: ManageConnection> Pool<M> {
pub(crate) fn new(manager: M, config: PoolConfig) -> Self {
Self {
shared: Arc::new(Shared {
manager,
config,
closed: AtomicBool::new(false),
metrics: Mutex::new(Vec::new()),
}),
}
}
pub fn builder() -> Builder<M> {
Builder::new()
}
pub fn max_size(&self) -> u32 {
self.shared.config.max_size
}
pub fn local(&self) -> LocalPool<M> {
LocalPool::new(self.shared.clone())
}
pub fn close(&self) {
self.shared.closed.store(true, Relaxed);
}
pub fn is_closed(&self) -> bool {
self.shared.closed.load(Relaxed)
}
pub fn state(&self) -> State {
let registry = self
.shared
.metrics
.lock()
.unwrap_or_else(PoisonError::into_inner);
let mut state = State::default();
for m in registry.iter() {
state.connections += m.connections.load(Relaxed);
state.idle_connections += m.idle.load(Relaxed);
state.pending_waiters += m.waiters.load(Relaxed);
}
state
}
pub fn statistics(&self) -> Statistics {
let registry = self
.shared
.metrics
.lock()
.unwrap_or_else(PoisonError::into_inner);
let mut stats = Statistics::default();
let mut micros = 0u64;
for m in registry.iter() {
stats.get_direct += m.get_direct.load(Relaxed);
stats.get_waited += m.get_waited.load(Relaxed);
stats.get_timed_out += m.get_timed_out.load(Relaxed);
stats.connections_created += m.connections_created.load(Relaxed);
stats.connections_closed_broken += m.connections_closed_broken.load(Relaxed);
stats.connections_closed_idle_timeout +=
m.connections_closed_idle_timeout.load(Relaxed);
stats.connections_closed_max_lifetime +=
m.connections_closed_max_lifetime.load(Relaxed);
micros += m.wait_micros.load(Relaxed);
}
stats.total_wait_time = Duration::from_micros(micros);
stats
}
}
impl<M: ManageConnection> fmt::Debug for Pool<M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Pool")
.field("max_size", &self.shared.config.max_size)
.field("closed", &self.is_closed())
.finish_non_exhaustive()
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct State {
pub connections: u32,
pub idle_connections: u32,
pub pending_waiters: u32,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Statistics {
pub get_direct: u64,
pub get_waited: u64,
pub get_timed_out: u64,
pub connections_created: u64,
pub connections_closed_broken: u64,
pub connections_closed_idle_timeout: u64,
pub connections_closed_max_lifetime: u64,
pub total_wait_time: Duration,
}
impl Statistics {
pub fn average_wait_time(&self) -> Duration {
self.total_wait_time
.checked_div(self.get_waited as u32)
.unwrap_or_default()
}
}
#[derive(Default)]
struct Metrics {
connections: AtomicU32,
idle: AtomicU32,
waiters: AtomicU32,
get_direct: AtomicU64,
get_waited: AtomicU64,
get_timed_out: AtomicU64,
connections_created: AtomicU64,
connections_closed_broken: AtomicU64,
connections_closed_idle_timeout: AtomicU64,
connections_closed_max_lifetime: AtomicU64,
wait_micros: AtomicU64,
}
impl Metrics {
fn inc_conn(&self) {
self.connections.fetch_add(1, Relaxed);
}
fn dec_conn(&self, n: u32) {
let c = self.connections.load(Relaxed);
self.connections.store(c.saturating_sub(n), Relaxed);
}
fn inc_idle(&self) {
self.idle.fetch_add(1, Relaxed);
}
fn dec_idle(&self, n: u32) {
let c = self.idle.load(Relaxed);
self.idle.store(c.saturating_sub(n), Relaxed);
}
fn note_expired(&self, reason: Expiry) {
match reason {
Expiry::IdleTimeout => self.connections_closed_idle_timeout.fetch_add(1, Relaxed),
Expiry::MaxLifetime => self.connections_closed_max_lifetime.fetch_add(1, Relaxed),
};
}
fn snapshot_state(&self) -> State {
State {
connections: self.connections.load(Relaxed),
idle_connections: self.idle.load(Relaxed),
pending_waiters: self.waiters.load(Relaxed),
}
}
fn snapshot_statistics(&self) -> Statistics {
Statistics {
get_direct: self.get_direct.load(Relaxed),
get_waited: self.get_waited.load(Relaxed),
get_timed_out: self.get_timed_out.load(Relaxed),
connections_created: self.connections_created.load(Relaxed),
connections_closed_broken: self.connections_closed_broken.load(Relaxed),
connections_closed_idle_timeout: self.connections_closed_idle_timeout.load(Relaxed),
connections_closed_max_lifetime: self.connections_closed_max_lifetime.load(Relaxed),
total_wait_time: Duration::from_micros(self.wait_micros.load(Relaxed)),
}
}
}
struct Conn<C> {
raw: C,
created_at: Instant,
last_used: Instant,
}
#[derive(Clone, Copy)]
enum Expiry {
IdleTimeout,
MaxLifetime,
}
fn expiry<C>(conn: &Conn<C>, now: Instant, cfg: &PoolConfig) -> Option<Expiry> {
if let Some(max) = cfg.max_lifetime
&& now.duration_since(conn.created_at) >= max
{
return Some(Expiry::MaxLifetime);
}
if let Some(idle) = cfg.idle_timeout
&& now.duration_since(conn.last_used) >= idle
{
return Some(Expiry::IdleTimeout);
}
None
}
struct Inner<M: ManageConnection> {
shared: Arc<Shared<M>>,
idle: RefCell<Vec<Conn<M::Connection>>>,
metrics: Arc<Metrics>,
waiters: Waiters,
}
impl<M: ManageConnection> Inner<M> {
fn connections(&self) -> u32 {
self.metrics.connections.load(Relaxed)
}
fn closed(&self) -> bool {
self.shared.closed.load(Relaxed)
}
fn release_slot(&self) {
self.metrics.dec_conn(1);
self.waiters.wake_all();
}
fn push_idle(&self, conn: Conn<M::Connection>) {
self.idle.borrow_mut().push(conn);
self.metrics.inc_idle();
}
fn pop_idle(&self) -> Option<Conn<M::Connection>> {
let conn = self.idle.borrow_mut().pop();
if conn.is_some() {
self.metrics.dec_idle(1);
}
conn
}
}
impl<M: ManageConnection> Drop for Inner<M> {
fn drop(&mut self) {
self.metrics.connections.store(0, Relaxed);
self.metrics.idle.store(0, Relaxed);
self.metrics.waiters.store(0, Relaxed);
}
}
pub struct LocalPool<M: ManageConnection> {
inner: Rc<Inner<M>>,
}
impl<M: ManageConnection> Clone for LocalPool<M> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<M: ManageConnection> LocalPool<M> {
pub(crate) fn new(shared: Arc<Shared<M>>) -> Self {
let cap = shared.config.max_size.min(1024) as usize;
let metrics = Arc::new(Metrics::default());
shared
.metrics
.lock()
.unwrap_or_else(PoisonError::into_inner)
.push(metrics.clone());
Self {
inner: Rc::new(Inner {
shared,
idle: RefCell::new(Vec::with_capacity(cap)),
waiters: Waiters::new(metrics.clone()),
metrics,
}),
}
}
pub fn max_size(&self) -> u32 {
self.inner.shared.config.max_size
}
pub fn state(&self) -> State {
self.inner.metrics.snapshot_state()
}
pub fn statistics(&self) -> Statistics {
self.inner.metrics.snapshot_statistics()
}
pub async fn warm(&self) -> Result<u32, RunError<M::Error>> {
if self.inner.closed() {
return Ok(0);
}
let target = self.inner.shared.config.min_idle;
let mut made = 0;
while self.inner.connections() < target {
let Some(mut held) = self.try_reserve() else {
break;
};
match self.inner.shared.manager.connect().await {
Ok(raw) => {
let now = Instant::now();
held.conn = Some(Conn {
raw,
created_at: now,
last_used: now,
});
self.inner.metrics.connections_created.fetch_add(1, Relaxed);
held.into_idle();
made += 1;
}
Err(e) => return Err(RunError::User(e)),
}
}
Ok(made)
}
pub fn reap(&self) -> u32 {
let now = Instant::now();
let cfg = &self.inner.shared.config;
let metrics = &self.inner.metrics;
let mut reaped = 0u32;
{
let mut idle = self.inner.idle.borrow_mut();
idle.retain(|conn| match expiry(conn, now, cfg) {
Some(reason) => {
metrics.note_expired(reason);
reaped += 1;
false
}
None => true,
});
}
if reaped > 0 {
metrics.dec_idle(reaped);
metrics.dec_conn(reaped);
self.inner.waiters.wake_all();
}
reaped
}
pub async fn maintain(&self) -> Result<u32, RunError<M::Error>> {
self.reap();
self.warm().await
}
pub fn clear(&self) -> u32 {
let n = {
let mut idle = self.inner.idle.borrow_mut();
let n = idle.len() as u32;
idle.clear();
n
};
if n > 0 {
self.inner.metrics.dec_idle(n);
self.inner.metrics.dec_conn(n);
self.inner.waiters.wake_all();
}
n
}
pub async fn get(&self) -> Result<PooledConnection<M>, RunError<M::Error>> {
if self.inner.closed() {
return Err(RunError::Closed);
}
let timeout = self.inner.shared.config.connection_timeout;
match compio::time::timeout(timeout, self.get_inner()).await {
Ok(result) => result,
Err(_elapsed) => {
self.inner.metrics.get_timed_out.fetch_add(1, Relaxed);
Err(RunError::TimedOut)
}
}
}
async fn get_inner(&self) -> Result<PooledConnection<M>, RunError<M::Error>> {
let start = Instant::now();
let mut waited = false;
loop {
if self.inner.closed() {
return Err(RunError::Closed);
}
waited |= self.wait_ready().await;
if self.inner.closed() {
return Err(RunError::Closed);
}
if let Some(mut held) = self.pop_fresh_idle() {
if self.inner.shared.config.test_on_check_out {
let valid = {
let conn = held.conn.as_mut().expect("idle held carries a connection");
self.inner
.shared
.manager
.is_valid(&mut conn.raw)
.await
.is_ok()
};
if !valid {
self.inner
.metrics
.connections_closed_broken
.fetch_add(1, Relaxed);
drop(held);
continue;
}
}
self.record_acquire(waited, start);
return Ok(held.into_pooled());
}
if let Some(mut held) = self.try_reserve() {
match self.inner.shared.manager.connect().await {
Ok(raw) => {
let now = Instant::now();
held.conn = Some(Conn {
raw,
created_at: now,
last_used: now,
});
self.inner.metrics.connections_created.fetch_add(1, Relaxed);
self.record_acquire(waited, start);
return Ok(held.into_pooled());
}
Err(e) => return Err(RunError::User(e)),
}
}
}
}
fn record_acquire(&self, waited: bool, start: Instant) {
if waited {
self.inner.metrics.get_waited.fetch_add(1, Relaxed);
self.inner
.metrics
.wait_micros
.fetch_add(start.elapsed().as_micros() as u64, Relaxed);
} else {
self.inner.metrics.get_direct.fetch_add(1, Relaxed);
}
}
fn try_reserve(&self) -> Option<Held<M>> {
let taken = self.inner.connections();
if taken < self.inner.shared.config.max_size {
self.inner.metrics.inc_conn();
Some(Held {
inner: self.inner.clone(),
conn: None,
released: false,
})
} else {
None
}
}
fn pop_fresh_idle(&self) -> Option<Held<M>> {
let now = Instant::now();
loop {
let conn = self.inner.pop_idle()?;
if let Some(reason) = expiry(&conn, now, &self.inner.shared.config) {
drop(conn);
self.inner.metrics.note_expired(reason);
self.inner.release_slot();
continue;
}
return Some(Held {
inner: self.inner.clone(),
conn: Some(conn),
released: false,
});
}
}
fn ready_now(&self) -> bool {
self.inner.closed()
|| self.inner.metrics.idle.load(Relaxed) > 0
|| self.inner.connections() < self.inner.shared.config.max_size
}
fn wait_ready(&self) -> WaitReady<'_, M> {
WaitReady {
pool: self,
id: None,
parked: false,
}
}
}
impl<M: ManageConnection> fmt::Debug for LocalPool<M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let state = self.state();
f.debug_struct("LocalPool")
.field("max_size", &self.inner.shared.config.max_size)
.field("connections", &state.connections)
.field("idle", &state.idle_connections)
.field("pending_waiters", &state.pending_waiters)
.finish()
}
}
struct Held<M: ManageConnection> {
inner: Rc<Inner<M>>,
conn: Option<Conn<M::Connection>>,
released: bool,
}
impl<M: ManageConnection> Held<M> {
fn into_pooled(mut self) -> PooledConnection<M> {
let conn = self.conn.take();
self.released = true;
PooledConnection {
inner: self.inner.clone(),
conn,
}
}
fn into_idle(mut self) {
if let Some(mut conn) = self.conn.take() {
conn.last_used = Instant::now();
self.inner.push_idle(conn);
self.inner.waiters.wake_all();
}
self.released = true;
}
}
impl<M: ManageConnection> Drop for Held<M> {
fn drop(&mut self) {
if !self.released {
self.conn.take();
self.inner.release_slot();
}
}
}
pub struct PooledConnection<M: ManageConnection> {
inner: Rc<Inner<M>>,
conn: Option<Conn<M::Connection>>,
}
impl<M: ManageConnection> Deref for PooledConnection<M> {
type Target = M::Connection;
fn deref(&self) -> &M::Connection {
&self
.conn
.as_ref()
.expect("connection present until drop")
.raw
}
}
impl<M: ManageConnection> DerefMut for PooledConnection<M> {
fn deref_mut(&mut self) -> &mut M::Connection {
&mut self
.conn
.as_mut()
.expect("connection present until drop")
.raw
}
}
impl<M: ManageConnection> Drop for PooledConnection<M> {
fn drop(&mut self) {
if let Some(mut conn) = self.conn.take() {
if self.inner.closed() {
drop(conn);
self.inner.release_slot();
return;
}
if self.inner.shared.manager.has_broken(&mut conn.raw) {
drop(conn);
self.inner
.metrics
.connections_closed_broken
.fetch_add(1, Relaxed);
self.inner.release_slot();
} else if let Some(reason) = expiry(&conn, Instant::now(), &self.inner.shared.config) {
drop(conn);
self.inner.metrics.note_expired(reason);
self.inner.release_slot();
} else {
conn.last_used = Instant::now();
self.inner.push_idle(conn);
self.inner.waiters.wake_all();
}
}
}
}
impl<M: ManageConnection> fmt::Debug for PooledConnection<M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PooledConnection")
.field("held", &self.conn.is_some())
.finish()
}
}
struct Waiters {
metrics: Arc<Metrics>,
next_id: Cell<u64>,
entries: RefCell<Vec<(u64, Waker)>>,
}
impl Waiters {
fn new(metrics: Arc<Metrics>) -> Self {
Self {
metrics,
next_id: Cell::new(0),
entries: RefCell::new(Vec::new()),
}
}
fn register(&self, id: &mut Option<u64>, waker: &Waker) {
let mut entries = self.entries.borrow_mut();
if let Some(existing) = *id
&& let Some(entry) = entries.iter_mut().find(|(i, _)| *i == existing)
{
if !entry.1.will_wake(waker) {
entry.1 = waker.clone();
}
return;
}
let new_id = self.next_id.get();
self.next_id.set(new_id.wrapping_add(1));
entries.push((new_id, waker.clone()));
self.metrics.waiters.store(entries.len() as u32, Relaxed);
*id = Some(new_id);
}
fn unregister(&self, id: Option<u64>) {
if let Some(id) = id {
let mut entries = self.entries.borrow_mut();
entries.retain(|(i, _)| *i != id);
self.metrics.waiters.store(entries.len() as u32, Relaxed);
}
}
fn wake_all(&self) {
let wakers: Vec<Waker> = self
.entries
.borrow_mut()
.drain(..)
.map(|(_, w)| w)
.collect();
self.metrics.waiters.store(0, Relaxed);
for waker in wakers {
waker.wake();
}
}
}
struct WaitReady<'a, M: ManageConnection> {
pool: &'a LocalPool<M>,
id: Option<u64>,
parked: bool,
}
impl<M: ManageConnection> Future for WaitReady<'_, M> {
type Output = bool;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<bool> {
let this = self.get_mut();
if this.pool.ready_now() {
this.pool.inner.waiters.unregister(this.id.take());
Poll::Ready(this.parked)
} else {
this.parked = true;
this.pool.inner.waiters.register(&mut this.id, cx.waker());
Poll::Pending
}
}
}
impl<M: ManageConnection> Drop for WaitReady<'_, M> {
fn drop(&mut self) {
self.pool.inner.waiters.unregister(self.id.take());
}
}