Skip to content

Commit 14e11bd

Browse files
authored
Merge pull request sfackler#944 from PG-kura/support-keepalive-interval-and-retries
Support keepalive interval and retries.
2 parents 5433118 + d7ccbb3 commit 14e11bd

File tree

9 files changed

+147
-17
lines changed

9 files changed

+147
-17
lines changed

postgres/src/config.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ use tokio_postgres::{Error, Socket};
4848
/// This option is ignored when connecting with Unix sockets. Defaults to on.
4949
/// * `keepalives_idle` - The number of seconds of inactivity after which a keepalive message is sent to the server.
5050
/// This option is ignored when connecting with Unix sockets. Defaults to 2 hours.
51+
/// * `keepalives_interval` - The time interval between TCP keepalive probes.
52+
/// This option is ignored when connecting with Unix sockets.
53+
/// * `keepalives_retries` - The maximum number of TCP keepalive probes that will be sent before dropping a connection.
54+
/// This option is ignored when connecting with Unix sockets.
5155
/// * `target_session_attrs` - Specifies requirements of the session. If set to `read-write`, the client will check that
5256
/// the `transaction_read_write` session parameter is set to `on`. This can be used to connect to the primary server
5357
/// in a database cluster as opposed to the secondary read-only mirrors. Defaults to `all`.
@@ -279,6 +283,33 @@ impl Config {
279283
self.config.get_keepalives_idle()
280284
}
281285

286+
/// Sets the time interval between TCP keepalive probes.
287+
/// On Windows, this sets the value of the tcp_keepalive struct’s keepaliveinterval field.
288+
///
289+
/// This is ignored for Unix domain sockets, or if the `keepalives` option is disabled.
290+
pub fn keepalives_interval(&mut self, keepalives_interval: Duration) -> &mut Config {
291+
self.config.keepalives_interval(keepalives_interval);
292+
self
293+
}
294+
295+
/// Gets the time interval between TCP keepalive probes.
296+
pub fn get_keepalives_interval(&self) -> Option<Duration> {
297+
self.config.get_keepalives_interval()
298+
}
299+
300+
/// Sets the maximum number of TCP keepalive probes that will be sent before dropping a connection.
301+
///
302+
/// This is ignored for Unix domain sockets, or if the `keepalives` option is disabled.
303+
pub fn keepalives_retries(&mut self, keepalives_retries: u32) -> &mut Config {
304+
self.config.keepalives_retries(keepalives_retries);
305+
self
306+
}
307+
308+
/// Gets the maximum number of TCP keepalive probes that will be sent before dropping a connection.
309+
pub fn get_keepalives_retries(&self) -> Option<u32> {
310+
self.config.get_keepalives_retries()
311+
}
312+
282313
/// Sets the requirements of the session.
283314
///
284315
/// This can be used to connect to the primary server in a clustered database rather than one of the read-only

tokio-postgres/src/cancel_query.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ where
3838
&config.host,
3939
config.port,
4040
config.connect_timeout,
41-
config.keepalives,
42-
config.keepalives_idle,
41+
config.keepalive.as_ref(),
4342
)
4443
.await?;
4544

tokio-postgres/src/client.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use crate::config::Host;
44
use crate::config::SslMode;
55
use crate::connection::{Request, RequestMessages};
66
use crate::copy_out::CopyOutStream;
7+
#[cfg(feature = "runtime")]
8+
use crate::keepalive::KeepaliveConfig;
79
use crate::query::RowStream;
810
use crate::simple_query::SimpleQueryStream;
911
#[cfg(feature = "runtime")]
@@ -154,8 +156,7 @@ pub(crate) struct SocketConfig {
154156
pub host: Host,
155157
pub port: u16,
156158
pub connect_timeout: Option<Duration>,
157-
pub keepalives: bool,
158-
pub keepalives_idle: Duration,
159+
pub keepalive: Option<KeepaliveConfig>,
159160
}
160161

161162
/// An asynchronous PostgreSQL client.

tokio-postgres/src/config.rs

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#[cfg(feature = "runtime")]
44
use crate::connect::connect;
55
use crate::connect_raw::connect_raw;
6+
use crate::keepalive::KeepaliveConfig;
67
#[cfg(feature = "runtime")]
78
use crate::tls::MakeTlsConnect;
89
use crate::tls::TlsConnect;
@@ -99,6 +100,10 @@ pub enum Host {
99100
/// This option is ignored when connecting with Unix sockets. Defaults to on.
100101
/// * `keepalives_idle` - The number of seconds of inactivity after which a keepalive message is sent to the server.
101102
/// This option is ignored when connecting with Unix sockets. Defaults to 2 hours.
103+
/// * `keepalives_interval` - The time interval between TCP keepalive probes.
104+
/// This option is ignored when connecting with Unix sockets.
105+
/// * `keepalives_retries` - The maximum number of TCP keepalive probes that will be sent before dropping a connection.
106+
/// This option is ignored when connecting with Unix sockets.
102107
/// * `target_session_attrs` - Specifies requirements of the session. If set to `read-write`, the client will check that
103108
/// the `transaction_read_write` session parameter is set to `on`. This can be used to connect to the primary server
104109
/// in a database cluster as opposed to the secondary read-only mirrors. Defaults to `all`.
@@ -156,7 +161,7 @@ pub struct Config {
156161
pub(crate) port: Vec<u16>,
157162
pub(crate) connect_timeout: Option<Duration>,
158163
pub(crate) keepalives: bool,
159-
pub(crate) keepalives_idle: Duration,
164+
pub(crate) keepalive_config: KeepaliveConfig,
160165
pub(crate) target_session_attrs: TargetSessionAttrs,
161166
pub(crate) channel_binding: ChannelBinding,
162167
}
@@ -170,6 +175,11 @@ impl Default for Config {
170175
impl Config {
171176
/// Creates a new configuration.
172177
pub fn new() -> Config {
178+
let keepalive_config = KeepaliveConfig {
179+
idle: Duration::from_secs(2 * 60 * 60),
180+
interval: None,
181+
retries: None,
182+
};
173183
Config {
174184
user: None,
175185
password: None,
@@ -181,7 +191,7 @@ impl Config {
181191
port: vec![],
182192
connect_timeout: None,
183193
keepalives: true,
184-
keepalives_idle: Duration::from_secs(2 * 60 * 60),
194+
keepalive_config,
185195
target_session_attrs: TargetSessionAttrs::Any,
186196
channel_binding: ChannelBinding::Prefer,
187197
}
@@ -347,14 +357,41 @@ impl Config {
347357
///
348358
/// This is ignored for Unix domain sockets, or if the `keepalives` option is disabled. Defaults to 2 hours.
349359
pub fn keepalives_idle(&mut self, keepalives_idle: Duration) -> &mut Config {
350-
self.keepalives_idle = keepalives_idle;
360+
self.keepalive_config.idle = keepalives_idle;
351361
self
352362
}
353363

354364
/// Gets the configured amount of idle time before a keepalive packet will
355365
/// be sent on the connection.
356366
pub fn get_keepalives_idle(&self) -> Duration {
357-
self.keepalives_idle
367+
self.keepalive_config.idle
368+
}
369+
370+
/// Sets the time interval between TCP keepalive probes.
371+
/// On Windows, this sets the value of the tcp_keepalive struct’s keepaliveinterval field.
372+
///
373+
/// This is ignored for Unix domain sockets, or if the `keepalives` option is disabled.
374+
pub fn keepalives_interval(&mut self, keepalives_interval: Duration) -> &mut Config {
375+
self.keepalive_config.interval = Some(keepalives_interval);
376+
self
377+
}
378+
379+
/// Gets the time interval between TCP keepalive probes.
380+
pub fn get_keepalives_interval(&self) -> Option<Duration> {
381+
self.keepalive_config.interval
382+
}
383+
384+
/// Sets the maximum number of TCP keepalive probes that will be sent before dropping a connection.
385+
///
386+
/// This is ignored for Unix domain sockets, or if the `keepalives` option is disabled.
387+
pub fn keepalives_retries(&mut self, keepalives_retries: u32) -> &mut Config {
388+
self.keepalive_config.retries = Some(keepalives_retries);
389+
self
390+
}
391+
392+
/// Gets the maximum number of TCP keepalive probes that will be sent before dropping a connection.
393+
pub fn get_keepalives_retries(&self) -> Option<u32> {
394+
self.keepalive_config.retries
358395
}
359396

360397
/// Sets the requirements of the session.
@@ -451,6 +488,20 @@ impl Config {
451488
self.keepalives_idle(Duration::from_secs(keepalives_idle as u64));
452489
}
453490
}
491+
"keepalives_interval" => {
492+
let keepalives_interval = value.parse::<i64>().map_err(|_| {
493+
Error::config_parse(Box::new(InvalidValue("keepalives_interval")))
494+
})?;
495+
if keepalives_interval > 0 {
496+
self.keepalives_interval(Duration::from_secs(keepalives_interval as u64));
497+
}
498+
}
499+
"keepalives_retries" => {
500+
let keepalives_retries = value.parse::<u32>().map_err(|_| {
501+
Error::config_parse(Box::new(InvalidValue("keepalives_retries")))
502+
})?;
503+
self.keepalives_retries(keepalives_retries);
504+
}
454505
"target_session_attrs" => {
455506
let target_session_attrs = match value {
456507
"any" => TargetSessionAttrs::Any,
@@ -545,7 +596,9 @@ impl fmt::Debug for Config {
545596
.field("port", &self.port)
546597
.field("connect_timeout", &self.connect_timeout)
547598
.field("keepalives", &self.keepalives)
548-
.field("keepalives_idle", &self.keepalives_idle)
599+
.field("keepalives_idle", &self.keepalive_config.idle)
600+
.field("keepalives_interval", &self.keepalive_config.interval)
601+
.field("keepalives_retries", &self.keepalive_config.retries)
549602
.field("target_session_attrs", &self.target_session_attrs)
550603
.field("channel_binding", &self.channel_binding)
551604
.finish()

tokio-postgres/src/connect.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,11 @@ where
6565
host,
6666
port,
6767
config.connect_timeout,
68-
config.keepalives,
69-
config.keepalives_idle,
68+
if config.keepalives {
69+
Some(&config.keepalive_config)
70+
} else {
71+
None
72+
},
7073
)
7174
.await?;
7275
let (mut client, mut connection) = connect_raw(socket, tls, config).await?;
@@ -115,8 +118,11 @@ where
115118
host: host.clone(),
116119
port,
117120
connect_timeout: config.connect_timeout,
118-
keepalives: config.keepalives,
119-
keepalives_idle: config.keepalives_idle,
121+
keepalive: if config.keepalives {
122+
Some(config.keepalive_config.clone())
123+
} else {
124+
None
125+
},
120126
});
121127

122128
Ok((client, connection))

tokio-postgres/src/connect_socket.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::config::Host;
2+
use crate::keepalive::KeepaliveConfig;
23
use crate::{Error, Socket};
34
use socket2::{SockRef, TcpKeepalive};
45
use std::future::Future;
@@ -13,8 +14,7 @@ pub(crate) async fn connect_socket(
1314
host: &Host,
1415
port: u16,
1516
connect_timeout: Option<Duration>,
16-
keepalives: bool,
17-
keepalives_idle: Duration,
17+
keepalive_config: Option<&KeepaliveConfig>,
1818
) -> Result<Socket, Error> {
1919
match host {
2020
Host::Tcp(host) => {
@@ -35,9 +35,9 @@ pub(crate) async fn connect_socket(
3535
};
3636

3737
stream.set_nodelay(true).map_err(Error::connect)?;
38-
if keepalives {
38+
if let Some(keepalive_config) = keepalive_config {
3939
SockRef::from(&stream)
40-
.set_tcp_keepalive(&TcpKeepalive::new().with_time(keepalives_idle))
40+
.set_tcp_keepalive(&TcpKeepalive::from(keepalive_config))
4141
.map_err(Error::connect)?;
4242
}
4343

tokio-postgres/src/keepalive.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use socket2::TcpKeepalive;
2+
use std::time::Duration;
3+
4+
#[derive(Clone, PartialEq, Eq)]
5+
pub(crate) struct KeepaliveConfig {
6+
pub idle: Duration,
7+
pub interval: Option<Duration>,
8+
pub retries: Option<u32>,
9+
}
10+
11+
impl From<&KeepaliveConfig> for TcpKeepalive {
12+
fn from(keepalive_config: &KeepaliveConfig) -> Self {
13+
let mut tcp_keepalive = Self::new().with_time(keepalive_config.idle);
14+
15+
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
16+
if let Some(interval) = keepalive_config.interval {
17+
tcp_keepalive = tcp_keepalive.with_interval(interval);
18+
}
19+
20+
#[cfg(not(any(target_os = "redox", target_os = "solaris", target_os = "windows")))]
21+
if let Some(retries) = keepalive_config.retries {
22+
tcp_keepalive = tcp_keepalive.with_retries(retries);
23+
}
24+
25+
tcp_keepalive
26+
}
27+
}

tokio-postgres/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ mod copy_in;
163163
mod copy_out;
164164
pub mod error;
165165
mod generic_client;
166+
mod keepalive;
166167
mod maybe_tls_stream;
167168
mod portal;
168169
mod prepare;

tokio-postgres/tests/test/parse.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ fn settings() {
3636
);
3737
}
3838

39+
#[test]
40+
fn keepalive_settings() {
41+
check(
42+
"keepalives=1 keepalives_idle=15 keepalives_interval=5 keepalives_retries=9",
43+
Config::new()
44+
.keepalives(true)
45+
.keepalives_idle(Duration::from_secs(15))
46+
.keepalives_interval(Duration::from_secs(5))
47+
.keepalives_retries(9),
48+
);
49+
}
50+
3951
#[test]
4052
fn url() {
4153
check("postgresql://", &Config::new());

0 commit comments

Comments
 (0)