@@ -16,6 +16,7 @@ pub use native_tls_opts::ClientIdentity;
1616pub use rustls_opts:: ClientIdentity ;
1717
1818use percent_encoding:: percent_decode;
19+ use rand:: Rng ;
1920use url:: { Host , Url } ;
2021
2122use std:: {
@@ -26,7 +27,7 @@ use std::{
2627 path:: Path ,
2728 str:: FromStr ,
2829 sync:: Arc ,
29- time:: Duration ,
30+ time:: { Duration , Instant } ,
3031 vec,
3132} ;
3233
@@ -209,6 +210,8 @@ pub struct PoolOpts {
209210 constraints : PoolConstraints ,
210211 inactive_connection_ttl : Duration ,
211212 ttl_check_interval : Duration ,
213+ abs_conn_ttl : Option < Duration > ,
214+ abs_conn_ttl_jitter : Option < Duration > ,
212215 reset_connection : bool ,
213216}
214217
@@ -273,6 +276,49 @@ impl PoolOpts {
273276 self . reset_connection
274277 }
275278
279+ /// Sets an absolute TTL after which a connection is removed from the pool.
280+ /// This may push the pool below the requested minimum pool size and is indepedent of the
281+ /// idle TTL.
282+ /// The absolute TTL is disabled by default.
283+ /// Fractions of seconds are ignored.
284+ pub fn with_abs_conn_ttl ( mut self , ttl : Option < Duration > ) -> Self {
285+ self . abs_conn_ttl = ttl;
286+ self
287+ }
288+
289+ /// Optionally, the absolute TTL can be extended by a per-connection random amount
290+ /// bounded by `jitter`.
291+ /// Setting `abs_conn_ttl_jitter` without `abs_conn_ttl` has no effect.
292+ /// Fractions of seconds are ignored.
293+ pub fn with_abs_conn_ttl_jitter ( mut self , jitter : Option < Duration > ) -> Self {
294+ self . abs_conn_ttl_jitter = jitter;
295+ self
296+ }
297+
298+ /// Returns the absolute TTL, if set.
299+ pub fn abs_conn_ttl ( & self ) -> Option < Duration > {
300+ self . abs_conn_ttl
301+ }
302+
303+ /// Returns the absolute TTL's jitter bound, if set.
304+ pub fn abs_conn_ttl_jitter ( & self ) -> Option < Duration > {
305+ self . abs_conn_ttl_jitter
306+ }
307+
308+ /// Returns a new deadline that's TTL (+ random jitter) in the future.
309+ pub ( crate ) fn new_connection_ttl_deadline ( & self ) -> Option < Instant > {
310+ if let Some ( ttl) = self . abs_conn_ttl {
311+ let jitter = if let Some ( jitter) = self . abs_conn_ttl_jitter {
312+ Duration :: from_secs ( rand:: thread_rng ( ) . gen_range ( 0 ..=jitter. as_secs ( ) ) )
313+ } else {
314+ Duration :: ZERO
315+ } ;
316+ Some ( Instant :: now ( ) + ttl + jitter)
317+ } else {
318+ None
319+ }
320+ }
321+
276322 /// Pool will recycle inactive connection if it is outside of the lower bound of the pool
277323 /// and if it is idling longer than this value (defaults to
278324 /// [`DEFAULT_INACTIVE_CONNECTION_TTL`]).
@@ -359,6 +405,8 @@ impl Default for PoolOpts {
359405 constraints : DEFAULT_POOL_CONSTRAINTS ,
360406 inactive_connection_ttl : DEFAULT_INACTIVE_CONNECTION_TTL ,
361407 ttl_check_interval : DEFAULT_TTL_CHECK_INTERVAL ,
408+ abs_conn_ttl : None ,
409+ abs_conn_ttl_jitter : None ,
362410 reset_connection : true ,
363411 }
364412 }
@@ -662,6 +710,49 @@ impl Opts {
662710 self . inner . mysql_opts . conn_ttl
663711 }
664712
713+ /// The pool will close a connection when this absolute TTL has elapsed.
714+ /// Disabled by default.
715+ ///
716+ /// Enables forced recycling and migration of connections in a guaranteed timeframe.
717+ /// This TTL bypasses pool constraints and an idle pool can go below the min size.
718+ ///
719+ /// # Connection URL
720+ ///
721+ /// You can use `abs_conn_ttl` URL parameter to set this value (in seconds). E.g.
722+ ///
723+ /// ```
724+ /// # use mysql_async::*;
725+ /// # use std::time::Duration;
726+ /// # fn main() -> Result<()> {
727+ /// let opts = Opts::from_url("mysql://localhost/db?abs_conn_ttl=86400")?;
728+ /// assert_eq!(opts.abs_conn_ttl(), Some(Duration::from_secs(24 * 60 * 60)));
729+ /// # Ok(()) }
730+ /// ```
731+ pub fn abs_conn_ttl ( & self ) -> Option < Duration > {
732+ self . inner . mysql_opts . pool_opts . abs_conn_ttl
733+ }
734+
735+ /// Upper bound of a random value added to the absolute TTL, if enabled.
736+ /// Disabled by default.
737+ ///
738+ /// Should be used to prevent connections from closing at the same time.
739+ ///
740+ /// # Connection URL
741+ ///
742+ /// You can use `abs_conn_ttl_jitter` URL parameter to set this value (in seconds). E.g.
743+ ///
744+ /// ```
745+ /// # use mysql_async::*;
746+ /// # use std::time::Duration;
747+ /// # fn main() -> Result<()> {
748+ /// let opts = Opts::from_url("mysql://localhost/db?abs_conn_ttl=7200&abs_conn_ttl_jitter=3600")?;
749+ /// assert_eq!(opts.abs_conn_ttl_jitter(), Some(Duration::from_secs(60 * 60)));
750+ /// # Ok(()) }
751+ /// ```
752+ pub fn abs_conn_ttl_jitter ( & self ) -> Option < Duration > {
753+ self . inner . mysql_opts . pool_opts . abs_conn_ttl_jitter
754+ }
755+
665756 /// Number of prepared statements cached on the client side (per connection). Defaults to
666757 /// [`DEFAULT_STMT_CACHE_SIZE`].
667758 ///
@@ -1444,6 +1535,34 @@ fn mysqlopts_from_url(url: &Url) -> std::result::Result<MysqlOpts, UrlError> {
14441535 } ) ;
14451536 }
14461537 }
1538+ } else if key == "abs_conn_ttl" {
1539+ match u64:: from_str ( & * value) {
1540+ Ok ( value) => {
1541+ opts. pool_opts = opts
1542+ . pool_opts
1543+ . with_abs_conn_ttl ( Some ( Duration :: from_secs ( value) ) )
1544+ }
1545+ _ => {
1546+ return Err ( UrlError :: InvalidParamValue {
1547+ param : "abs_conn_ttl" . into ( ) ,
1548+ value,
1549+ } ) ;
1550+ }
1551+ }
1552+ } else if key == "abs_conn_ttl_jitter" {
1553+ match u64:: from_str ( & * value) {
1554+ Ok ( value) => {
1555+ opts. pool_opts = opts
1556+ . pool_opts
1557+ . with_abs_conn_ttl_jitter ( Some ( Duration :: from_secs ( value) ) )
1558+ }
1559+ _ => {
1560+ return Err ( UrlError :: InvalidParamValue {
1561+ param : "abs_conn_ttl_jitter" . into ( ) ,
1562+ value,
1563+ } ) ;
1564+ }
1565+ }
14471566 } else if key == "tcp_keepalive" {
14481567 match u32:: from_str ( & * value) {
14491568 Ok ( value) => opts. tcp_keepalive = Some ( value) ,
@@ -1679,6 +1798,11 @@ mod test {
16791798 assert_eq ! ( url_opts. tcp_nodelay( ) , builder_opts. tcp_nodelay( ) ) ;
16801799 assert_eq ! ( url_opts. pool_opts( ) , builder_opts. pool_opts( ) ) ;
16811800 assert_eq ! ( url_opts. conn_ttl( ) , builder_opts. conn_ttl( ) ) ;
1801+ assert_eq ! ( url_opts. abs_conn_ttl( ) , builder_opts. abs_conn_ttl( ) ) ;
1802+ assert_eq ! (
1803+ url_opts. abs_conn_ttl_jitter( ) ,
1804+ builder_opts. abs_conn_ttl_jitter( )
1805+ ) ;
16821806 assert_eq ! ( url_opts. stmt_cache_size( ) , builder_opts. stmt_cache_size( ) ) ;
16831807 assert_eq ! ( url_opts. ssl_opts( ) , builder_opts. ssl_opts( ) ) ;
16841808 assert_eq ! ( url_opts. prefer_socket( ) , builder_opts. prefer_socket( ) ) ;
0 commit comments