From 39cbf2b3c94635ce64b7bb3dcb876f83b8249574 Mon Sep 17 00:00:00 2001 From: fulara Date: Fri, 18 Feb 2022 01:17:06 +0900 Subject: [PATCH] keepalive: add setting of interval between probes. --- src/conn/mod.rs | 4 ++++ src/conn/opts/mod.rs | 55 +++++++++++++++++++++++++++++++++++++++++++- src/io/mod.rs | 4 ++++ src/io/tcp.rs | 32 ++++++++++++++++++++++++++ src/lib.rs | 2 ++ 5 files changed, 96 insertions(+), 1 deletion(-) diff --git a/src/conn/mod.rs b/src/conn/mod.rs index b9e61c4..3f4fef7 100644 --- a/src/conn/mod.rs +++ b/src/conn/mod.rs @@ -392,6 +392,8 @@ impl Conn { let write_timeout = opts.get_write_timeout().cloned(); let tcp_keepalive_time = opts.get_tcp_keepalive_time_ms(); #[cfg(any(target_os = "linux", target_os = "macos",))] + let tcp_keepalive_probe_interval_secs = opts.get_tcp_keepalive_probe_interval_secs(); + #[cfg(any(target_os = "linux", target_os = "macos",))] let tcp_keepalive_probe_count = opts.get_tcp_keepalive_probe_count(); #[cfg(target_os = "linux")] let tcp_user_timeout = opts.get_tcp_user_timeout_ms(); @@ -414,6 +416,8 @@ impl Conn { write_timeout, tcp_keepalive_time, #[cfg(any(target_os = "linux", target_os = "macos",))] + tcp_keepalive_probe_interval_secs, + #[cfg(any(target_os = "linux", target_os = "macos",))] tcp_keepalive_probe_count, #[cfg(target_os = "linux")] tcp_user_timeout, diff --git a/src/conn/opts/mod.rs b/src/conn/opts/mod.rs index 9bdede4..ddff86c 100644 --- a/src/conn/opts/mod.rs +++ b/src/conn/opts/mod.rs @@ -134,6 +134,12 @@ pub(crate) struct InnerOpts { /// Can be defined using `tcp_keepalive_time_ms` connection url parameter. tcp_keepalive_time: Option, + /// TCP keep alive interval between subsequent probe for mysql connection. + /// + /// Can be defined using `tcp_keepalive_probe_interval_secs` connection url parameter. + #[cfg(any(target_os = "linux", target_os = "macos",))] + tcp_keepalive_probe_interval_secs: Option, + /// TCP keep alive probe count for mysql connection. /// /// Can be defined using `tcp_keepalive_probe_count` connection url parameter. @@ -229,6 +235,8 @@ impl Default for InnerOpts { ssl_opts: None, tcp_keepalive_time: None, #[cfg(any(target_os = "linux", target_os = "macos",))] + tcp_keepalive_probe_interval_secs: None, + #[cfg(any(target_os = "linux", target_os = "macos",))] tcp_keepalive_probe_count: None, #[cfg(target_os = "linux")] tcp_user_timeout: None, @@ -336,6 +344,12 @@ impl Opts { self.0.tcp_keepalive_time } + /// TCP keep alive interval between subsequent probes for mysql connection. + #[cfg(any(target_os = "linux", target_os = "macos",))] + pub fn get_tcp_keepalive_probe_interval_secs(&self) -> Option { + self.0.tcp_keepalive_probe_interval_secs + } + /// TCP keep alive probe count for mysql connection. #[cfg(any(target_os = "linux", target_os = "macos",))] pub fn get_tcp_keepalive_probe_count(&self) -> Option { @@ -504,6 +518,7 @@ impl OptsBuilder { /// - db_name = Database name (defaults to `None`). /// - prefer_socket = Prefer socket connection (defaults to `true`) /// - tcp_keepalive_time_ms = TCP keep alive time for mysql connection (defaults to `None`) + /// - tcp_keepalive_probe_interval_secs = TCP keep alive interval between probes for mysql connection (defaults to `None`) /// - tcp_keepalive_probe_count = TCP keep alive probe count for mysql connection (defaults to `None`) /// - tcp_user_timeout_ms = TCP_USER_TIMEOUT time for mysql connection (defaults to `None`) /// - compress = Compression level(defaults to `None`) @@ -557,6 +572,16 @@ impl OptsBuilder { } } #[cfg(any(target_os = "linux", target_os = "macos",))] + "tcp_keepalive_probe_interval_secs" => { + //if cannot parse, default to none + self.opts.0.tcp_keepalive_probe_interval_secs = match value.parse::() { + Ok(val) => Some(val), + _ => { + return Err(UrlError::InvalidValue(key.to_string(), value.to_string())) + } + } + } + #[cfg(any(target_os = "linux", target_os = "macos",))] "tcp_keepalive_probe_count" => { //if cannot parse, default to none self.opts.0.tcp_keepalive_probe_count = match value.parse::() { @@ -686,6 +711,19 @@ impl OptsBuilder { self } + /// TCP keep alive interval between probes for mysql connection (defaults to `None`). Available as + /// `tcp_keepalive_probe_interval_secs` url parameter. + /// + /// Can be defined using `tcp_keepalive_probe_interval_secs` connection url parameter. + #[cfg(any(target_os = "linux", target_os = "macos",))] + pub fn tcp_keepalive_probe_interval_secs( + mut self, + tcp_keepalive_probe_interval_secs: Option, + ) -> Self { + self.opts.0.tcp_keepalive_probe_interval_secs = tcp_keepalive_probe_interval_secs; + self + } + /// TCP keep alive probe count for mysql connection (defaults to `None`). Available as /// `tcp_keepalive_probe_count` url parameter. /// @@ -991,6 +1029,11 @@ mod test { #[test] fn should_convert_url_into_opts() { + #[cfg(any(target_os = "linux", target_os = "macos",))] + let tcp_keepalive_probe_interval_secs = "&tcp_keepalive_probe_interval_secs=8"; + #[cfg(not(any(target_os = "linux", target_os = "macos",)))] + let tcp_keepalive_probe_interval_secs = ""; + #[cfg(any(target_os = "linux", target_os = "macos",))] let tcp_keepalive_probe_count = "&tcp_keepalive_probe_count=5"; #[cfg(not(any(target_os = "linux", target_os = "macos",)))] @@ -1002,7 +1045,8 @@ mod test { let tcp_user_timeout = ""; let opts = format!( - "mysql://us%20r:p%20w@localhost:3308/db%2dname?prefer_socket=false&tcp_keepalive_time_ms=5000{}{}&socket=%2Ftmp%2Fmysql.sock&compress=8", + "mysql://us%20r:p%20w@localhost:3308/db%2dname?prefer_socket=false&tcp_keepalive_time_ms=5000{}{}{}&socket=%2Ftmp%2Fmysql.sock&compress=8", + tcp_keepalive_probe_interval_secs, tcp_keepalive_probe_count, tcp_user_timeout, ); @@ -1016,6 +1060,8 @@ mod test { prefer_socket: false, tcp_keepalive_time: Some(5000), #[cfg(any(target_os = "linux", target_os = "macos",))] + tcp_keepalive_probe_interval_secs: Some(8), + #[cfg(any(target_os = "linux", target_os = "macos",))] tcp_keepalive_probe_count: Some(5), #[cfg(target_os = "linux")] tcp_user_timeout: Some(6000), @@ -1072,6 +1118,8 @@ mod test { "prefer_socket".to_string() => "false".to_string(), "tcp_keepalive_time_ms".to_string() => "5000".to_string(), #[cfg(any(target_os = "linux", target_os = "macos",))] + "tcp_keepalive_probe_interval_secs".to_string() => "8".to_string(), + #[cfg(any(target_os = "linux", target_os = "macos",))] "tcp_keepalive_probe_count".to_string() => "5".to_string(), "compress".to_string() => "best".to_string(), "tcp_connect_timeout_ms".to_string() => "1000".to_string(), @@ -1088,6 +1136,11 @@ mod test { assert_eq!(parsed_opts.opts.get_prefer_socket(), false); assert_eq!(parsed_opts.opts.get_tcp_keepalive_time_ms(), Some(5000)); #[cfg(any(target_os = "linux", target_os = "macos",))] + assert_eq!( + parsed_opts.opts.get_tcp_keepalive_probe_interval_secs(), + Some(8) + ); + #[cfg(any(target_os = "linux", target_os = "macos",))] assert_eq!(parsed_opts.opts.get_tcp_keepalive_probe_count(), Some(5)); assert_eq!( parsed_opts.opts.get_compress(), diff --git a/src/io/mod.rs b/src/io/mod.rs index 8f5e578..d87908b 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -96,6 +96,8 @@ impl Stream { read_timeout: Option, write_timeout: Option, tcp_keepalive_time: Option, + #[cfg(any(target_os = "linux", target_os = "macos",))] + tcp_keepalive_probe_interval_secs: Option, #[cfg(any(target_os = "linux", target_os = "macos",))] tcp_keepalive_probe_count: Option< u32, >, @@ -113,6 +115,8 @@ impl Stream { .nodelay(nodelay) .bind_address(bind_address); #[cfg(any(target_os = "linux", target_os = "macos",))] + builder.keepalive_probe_interval_secs(tcp_keepalive_probe_interval_secs); + #[cfg(any(target_os = "linux", target_os = "macos",))] builder.keepalive_probe_count(tcp_keepalive_probe_count); #[cfg(target_os = "linux")] builder.user_timeout(tcp_user_timeout); diff --git a/src/io/tcp.rs b/src/io/tcp.rs index 45329ba..1523a5a 100644 --- a/src/io/tcp.rs +++ b/src/io/tcp.rs @@ -22,6 +22,8 @@ pub struct MyTcpBuilder { write_timeout: Option, keepalive_time_ms: Option, #[cfg(any(target_os = "linux", target_os = "macos",))] + keepalive_probe_interval_secs: Option, + #[cfg(any(target_os = "linux", target_os = "macos",))] keepalive_probe_count: Option, #[cfg(target_os = "linux")] user_timeout: Option, @@ -34,6 +36,15 @@ impl MyTcpBuilder { self } + #[cfg(any(target_os = "linux", target_os = "macos",))] + pub fn keepalive_probe_interval_secs( + &mut self, + keepalive_probe_interval_secs: Option, + ) -> &mut Self { + self.keepalive_probe_interval_secs = keepalive_probe_interval_secs; + self + } + #[cfg(any(target_os = "linux", target_os = "macos",))] pub fn keepalive_probe_count(&mut self, keepalive_probe_count: Option) -> &mut Self { self.keepalive_probe_count = keepalive_probe_count; @@ -83,6 +94,8 @@ impl MyTcpBuilder { write_timeout: None, keepalive_time_ms: None, #[cfg(any(target_os = "linux", target_os = "macos",))] + keepalive_probe_interval_secs: None, + #[cfg(any(target_os = "linux", target_os = "macos",))] keepalive_probe_count: None, #[cfg(target_os = "linux")] user_timeout: None, @@ -98,6 +111,8 @@ impl MyTcpBuilder { read_timeout, write_timeout, keepalive_time_ms, + #[cfg(any(target_os = "linux", target_os = "macos"))] + keepalive_probe_interval_secs, #[cfg(any(target_os = "linux", target_os = "macos",))] keepalive_probe_count, #[cfg(target_os = "linux")] @@ -170,6 +185,23 @@ impl MyTcpBuilder { socket2::TcpKeepalive::new().with_time(Duration::from_millis(duration as u64)); socket.set_tcp_keepalive(&conf)?; } + #[cfg(any(target_os = "linux", target_os = "macos",))] + if let Some(keepalive_probe_interval_secs) = keepalive_probe_interval_secs { + use std::os::unix::io::AsRawFd; + let fd = socket.as_raw_fd(); + unsafe { + if libc::setsockopt( + fd, + libc::IPPROTO_TCP, + libc::TCP_KEEPINTVL, + &keepalive_probe_interval_secs as *const _ as *const libc::c_void, + std::mem::size_of_val(&keepalive_probe_interval_secs) as libc::socklen_t, + ) != 0 + { + return Err(io::Error::last_os_error()); + } + } + } #[cfg(any(target_os = "linux", target_os = "macos"))] if let Some(keepalive_probe_count) = keepalive_probe_count { use std::os::unix::io::AsRawFd; diff --git a/src/lib.rs b/src/lib.rs index 1040ee6..20901ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -175,6 +175,8 @@ //! * `prefer_socket: true | false` - defines the value of the same field in the `Opts` structure; //! * `tcp_keepalive_time_ms: u32` - defines the value (in milliseconds) //! of the `tcp_keepalive_time` field in the `Opts` structure; +//! * `tcp_keepalive_probe_interval_secs: u32` - defines the value +//! of the `tcp_keepalive_probe_interval_secs` field in the `Opts` structure; //! * `tcp_keepalive_probe_count: u32` - defines the value //! of the `tcp_keepalive_probe_count` field in the `Opts` structure; //! * `tcp_connect_timeout_ms: u64` - defines the value (in milliseconds)