mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-10-03 07:45:30 +00:00
implement pool closing
This commit is contained in:
parent
812c0bee61
commit
e155aa7aea
@ -41,6 +41,9 @@ pub enum Error {
|
|||||||
/// because another task encountered too many errors while trying to open a new connection.
|
/// because another task encountered too many errors while trying to open a new connection.
|
||||||
TimedOut,
|
TimedOut,
|
||||||
|
|
||||||
|
/// `Pool::close()` was called while we were waiting in `Pool::acquire()`.
|
||||||
|
PoolClosed,
|
||||||
|
|
||||||
// TODO: Remove and replace with `#[non_exhaustive]` when possible
|
// TODO: Remove and replace with `#[non_exhaustive]` when possible
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
__Nonexhaustive,
|
__Nonexhaustive,
|
||||||
|
@ -15,7 +15,7 @@ use std::{
|
|||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicU32, AtomicUsize, Ordering},
|
atomic::{AtomicU32, AtomicUsize, AtomicBool, Ordering},
|
||||||
Arc,
|
Arc,
|
||||||
},
|
},
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
@ -68,7 +68,7 @@ where
|
|||||||
///
|
///
|
||||||
/// Does not resolve until all connections are closed.
|
/// Does not resolve until all connections are closed.
|
||||||
pub async fn close(&self) {
|
pub async fn close(&self) {
|
||||||
unimplemented!()
|
let _ = self.0.close().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of connections currently being managed by the pool.
|
/// Returns the number of connections currently being managed by the pool.
|
||||||
@ -195,6 +195,7 @@ where
|
|||||||
pool_rx: Receiver<Idle<DB>>,
|
pool_rx: Receiver<Idle<DB>>,
|
||||||
pool_tx: Sender<Idle<DB>>,
|
pool_tx: Sender<Idle<DB>>,
|
||||||
size: AtomicU32,
|
size: AtomicU32,
|
||||||
|
closed: AtomicBool,
|
||||||
options: Options,
|
options: Options,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,12 +213,35 @@ where
|
|||||||
pool_rx,
|
pool_rx,
|
||||||
pool_tx,
|
pool_tx,
|
||||||
size: AtomicU32::new(0),
|
size: AtomicU32::new(0),
|
||||||
|
closed: AtomicBool::new(false),
|
||||||
options,
|
options,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn close(&self) {
|
||||||
|
self.closed.store(true, Ordering::Release);
|
||||||
|
|
||||||
|
while self.size.load(Ordering::Acquire) > 0 {
|
||||||
|
// don't block on the receiver because we own one Sender so it should never return
|
||||||
|
// `None`; a `select!()` would also work but that produces more complicated code
|
||||||
|
// and a timeout isn't necessarily appropriate
|
||||||
|
match self.pool_rx.recv().now_or_never() {
|
||||||
|
Some(Some(idle)) => {
|
||||||
|
let _ = idle.raw.inner.close().await;
|
||||||
|
self.size.fetch_sub(1, Ordering::AcqRel);
|
||||||
|
},
|
||||||
|
Some(None) => panic!("we own a Sender how did this happen"),
|
||||||
|
None => task::yield_now().await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn try_acquire(&self) -> Option<Live<DB>> {
|
fn try_acquire(&self) -> Option<Live<DB>> {
|
||||||
|
if self.closed.load(Ordering::Acquire) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
Some(self.pool_rx.recv().now_or_never()??.live(&self.pool_tx))
|
Some(self.pool_rx.recv().now_or_never()??.live(&self.pool_tx))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +253,7 @@ where
|
|||||||
return Ok(live);
|
return Ok(live);
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
while !self.closed.load(Ordering::Acquire) {
|
||||||
let size = self.size.load(Ordering::Acquire);
|
let size = self.size.load(Ordering::Acquire);
|
||||||
|
|
||||||
if size >= self.options.max_size {
|
if size >= self.options.max_size {
|
||||||
@ -248,6 +272,12 @@ where
|
|||||||
Err(_) => continue,
|
Err(_) => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if self.closed.load(Ordering::Acquire) {
|
||||||
|
let _ = idle.raw.inner.close().await;
|
||||||
|
self.size.fetch_sub(1, Ordering::AcqRel);
|
||||||
|
return Err(Error::PoolClosed);
|
||||||
|
}
|
||||||
|
|
||||||
// check if idle connection was within max lifetime (or not set)
|
// check if idle connection was within max lifetime (or not set)
|
||||||
if self.options.max_lifetime.map_or(true, |max| idle.raw.created.elapsed() < max)
|
if self.options.max_lifetime.map_or(true, |max| idle.raw.created.elapsed() < max)
|
||||||
// and if connection wasn't idle too long (or not set)
|
// and if connection wasn't idle too long (or not set)
|
||||||
@ -277,10 +307,17 @@ where
|
|||||||
return self.new_conn(deadline).await
|
return self.new_conn(deadline).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Err(Error::PoolClosed)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn new_conn(&self, deadline: Instant) -> crate::Result<Live<DB>> {
|
async fn new_conn(&self, deadline: Instant) -> crate::Result<Live<DB>> {
|
||||||
while Instant::now() < deadline {
|
while Instant::now() < deadline {
|
||||||
|
if self.closed.load(Ordering::Acquire) {
|
||||||
|
self.size.fetch_sub(1, Ordering::AcqRel);
|
||||||
|
return Err(Error::PoolClosed);
|
||||||
|
}
|
||||||
|
|
||||||
// result here is `Result<Result<DB, Error>, TimeoutError>`
|
// result here is `Result<Result<DB, Error>, TimeoutError>`
|
||||||
match timeout(deadline - Instant::now(), DB::open(&self.url)).await {
|
match timeout(deadline - Instant::now(), DB::open(&self.url)).await {
|
||||||
Ok(Ok(raw)) => return Ok(Live::pooled(raw, &self.pool_tx)),
|
Ok(Ok(raw)) => return Ok(Live::pooled(raw, &self.pool_tx)),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user