diff --git a/sqlx-core/src/pool/inner.rs b/sqlx-core/src/pool/inner.rs index 529d3566..f6232586 100644 --- a/sqlx-core/src/pool/inner.rs +++ b/sqlx-core/src/pool/inner.rs @@ -38,8 +38,6 @@ where url: &str, options: Options, ) -> crate::Result<(Arc, Sender>)> { - // TODO: Establish [min_idle] connections - let (pool_tx, pool_rx) = channel(options.max_size as usize); let pool = Arc::new(Self { @@ -50,6 +48,18 @@ where options, }); + for _ in 0.. pool.options.min_size { + let raw = pool.new_conn( + Instant::now() + pool.options.connect_timeout + ).await?; + + pool_tx.send(Idle { + raw, + since: Instant::now() + }) + .await; + } + conn_reaper(&pool, &pool_tx); Ok((pool, pool_tx)) @@ -225,7 +235,7 @@ where let max_reaped = pool .size .load(Ordering::Acquire) - .saturating_sub(pool.options.min_idle); + .saturating_sub(pool.options.min_size); // collect connections to reap let (reap, keep) = (0..max_reaped) diff --git a/sqlx-core/src/pool/mod.rs b/sqlx-core/src/pool/mod.rs index b58bf517..2632b6ad 100644 --- a/sqlx-core/src/pool/mod.rs +++ b/sqlx-core/src/pool/mod.rs @@ -50,7 +50,7 @@ where { /// Creates a connection pool with the default configuration. pub async fn new(url: &str) -> crate::Result { - Self::with_options(url, Options::default()).await + Self::builder().build(url).await } async fn with_options(url: &str, options: Options) -> crate::Result { @@ -76,8 +76,7 @@ where /// Attempts to retrieve a connection from the pool if there is one available. /// - /// Returns `None` if there are no idle connections available in the pool. - /// This method will not block waiting to establish a new connection. + /// Returns `None` immediately if there are no idle connections available in the pool. pub fn try_acquire(&self) -> Option> { self.inner.try_acquire().map(|conn| Connection { raw: Some(conn), @@ -114,8 +113,8 @@ where } /// Returns the configured minimum idle connection count. - pub fn min_idle(&self) -> u32 { - self.inner.options().min_idle + pub fn min_size(&self) -> u32 { + self.inner.options().min_size } /// Returns the configured maximum connection lifetime. diff --git a/sqlx-core/src/pool/options.rs b/sqlx-core/src/pool/options.rs index d562475f..c11ff576 100644 --- a/sqlx-core/src/pool/options.rs +++ b/sqlx-core/src/pool/options.rs @@ -4,7 +4,7 @@ use crate::Database; use super::Pool; -#[derive(Default)] +/// Builder for [Pool]. pub struct Builder where DB: Database, @@ -17,62 +17,101 @@ impl Builder where DB: Database, { + /// Get a new builder with default options. + /// + /// See the source of this method for current defaults. pub fn new() -> Self { Self { phantom: PhantomData, - options: Options::default(), + options: Options { + // pool a maximum of 10 connections to the same database + max_size: 10, + // don't open connections until necessary + min_size: 0, + // try to connect for 30 seconds before erroring + connect_timeout: Duration::from_secs(30), + // reap connections that have been alive > 30 minutes + // prevents unbounded live-leaking of memory due to naive prepared statement caching + // see src/cache.rs for context + max_lifetime: Some(Duration::from_secs(1800)), + // don't reap connections based on idle time + idle_timeout: None, + }, } } + /// Set the maximum number of connections that this pool should maintain. pub fn max_size(mut self, max_size: u32) -> Self { self.options.max_size = max_size; self } + /// Set the amount of time to attempt connecting to the database. + /// + /// If this timeout elapses, [Pool::acquire] will return an error. pub fn connect_timeout(mut self, connect_timeout: Duration) -> Self { self.options.connect_timeout = connect_timeout; self } - pub fn min_idle(mut self, min_idle: u32) -> Self { - self.options.min_idle = min_idle; + /// Set the minimum number of connections to maintain at all times. + /// + /// When the pool is built, this many connections will be automatically spun up. + /// + /// If any connection is reaped by [max_lifetime] or [idle_timeout] and it brings + /// the connection count below this amount, a new connection will be opened to replace it. + pub fn min_size(mut self, min_size: u32) -> Self { + self.options.min_size = min_size; self } + /// Set the maximum lifetime of individual connections. + /// + /// Any connection with a lifetime greater than this will be closed. + /// + /// When set to `None`, all connections live until either reaped by [idle_timeout] + /// or explicitly disconnected. + /// + /// Infinite connections are not recommended due to the unfortunate reality of memory/resource + /// leaks on the database-side. It is better to retire connections periodically + /// (even if only once daily) to allow the database the opportunity to clean up data structures + /// (parse trees, query metadata caches, thread-local storage, etc.) that are associated with a + /// session. pub fn max_lifetime(mut self, max_lifetime: impl Into>) -> Self { self.options.max_lifetime = max_lifetime.into(); self } + /// Set a maximum idle duration for individual connections. + /// + /// Any connection with an idle duration longer than this will be closed. + /// + /// For usage-based database server billing, this can be a cost saver. pub fn idle_timeout(mut self, idle_timeout: impl Into>) -> Self { self.options.idle_timeout = idle_timeout.into(); self } + /// Spin up the connection pool. + /// + /// If [min_size] was set to a non-zero value, that many connections will be immediately + /// opened and placed into the pool. pub async fn build(self, url: &str) -> crate::Result> { Pool::with_options(url, self.options).await } } +impl Default for Builder + where + DB: Database +{ + fn default() -> Self { Self::new() } +} + pub(crate) struct Options { pub max_size: u32, pub connect_timeout: Duration, - pub min_idle: u32, + pub min_size: u32, pub max_lifetime: Option, pub idle_timeout: Option, } - -impl Default for Options { - fn default() -> Self { - Self { - max_size: 10, - min_idle: 0, - connect_timeout: Duration::from_secs(30), - // 30 minutes - // prevents unbounded live-leaking of memory due to naive prepared statement caching - // see src/cache.rs for context - max_lifetime: Some(Duration::from_secs(1800)), - idle_timeout: None, - } - } -}